Android MVVM探索(一) - DataBiding初解

發(fā)現(xiàn)我好久沒(méi)寫博客,其實(shí)最近一直都很想寫博客的,但是不知道寫點(diǎn)什么好餐抢。剛好碰上最近在學(xué)習(xí)Android的MVVM設(shè)計(jì)模式以及官方提供給我們的控件,所以才有了這篇文章低匙。(其實(shí)還是因?yàn)槲覒锌鹾郏覒校。?/h5>

今天我想給大家講講DataBinding顽冶,為了保證我寫的不會(huì)出錯(cuò)欺抗,我也借鑒參考了不少文章和視頻。給大家看看一篇我個(gè)人覺(jué)得還不錯(cuò)的强重。DataBinding最全使用說(shuō)明(掘金博客)還有某課網(wǎng)的視頻绞呈,Android Data Binding實(shí)戰(zhàn)-入門篇Android Data Binding實(shí)戰(zhàn)-高級(jí)篇

本篇文章代碼地址

Android MVVM探索系列

Android MVVM探索(一) - DataBiding初解

Android MVVM探索(二) - DataBiding常用注解

Android MVVM探索(三) - ViewModel间景,DataBinding佃声,LiveData混合三打

1, 什么是DataBinding?

DataBinding,2015年IO大會(huì)介紹的一個(gè)框架倘要,字面理解即為數(shù)據(jù)綁定圾亏,是Google對(duì)MVVM在Android上的一種實(shí)現(xiàn),可以直接綁定數(shù)據(jù)到xml中碗誉,并實(shí)現(xiàn)自動(dòng)刷新(即召嘶,數(shù)據(jù)變化UI進(jìn)行相應(yīng)的變化)。而且還支持一些表達(dá)式哮缺。比如常見(jiàn)的三元運(yùn)算符:

1+x == 3 ? "true" : "false"

它還可以支持lambda表達(dá)式:

(v,fcs) -> presenter.onFocusChange(user)}

使用了DataBinding,可以省去一些控件綁定代碼甲喝,例如:findviewById等尝苇。

2, 開(kāi)始使用DataBinding

要想使用DataBinding的話,首先要在你安卓工程中,安卓Application的module(一般為app這個(gè)module)的android配置中加上如下代碼:

android{
    // 這里省去一些常有的配置代碼
    dataBinding {
        enabled = true;
    }
}

另外糠溜,如果你是使用Kotlin進(jìn)行編程的話淳玩,你還要在加入了上面代碼的Gradle文件中頂部加上以下代碼,否則Kotlin將無(wú)法識(shí)別DataBinding資源非竿,至于什么是DataBinding資源蜕着,我們后面會(huì)提到:

apply plugin: 'kotlin-kapt'

是的,你沒(méi)有看錯(cuò)红柱,就這么簡(jiǎn)單我們就加上了DataBiding承匣,不需要引入任何依賴。
首先我們先建立一個(gè)普通類作為ViewModel:

package top.cyixlq.test

class MainViewModel {
    var name = "張三"
    var age = 15
    var isMan = true
    fun log() {
        Log.d("MyTAG", "按鈕被點(diǎn)擊了一下")
    }
}

其次锤悄,我們要將我們布局文件代碼進(jìn)行一些改動(dòng)韧骗。

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <data>
        <variable
            name="viewModel"
            type="top.cyixlq.test.MainViewModel"/>
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context=".MainActivity">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{viewModel.name}"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{String.valueOf(viewModel.age)}"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{viewModel.isMan ? @string/man : @string/woman}"/>

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="@{v -> viewModel.log()}"
            android:text="點(diǎn)我"/>

    </LinearLayout>
</layout>

最后,改造我們的Activity代碼:

class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
        // 第一種將數(shù)據(jù)填充到xml文件中的方法(代碼在下面這行)零聚,我們直接實(shí)例化了一個(gè)MainViewModel賦值給BR資源中一個(gè)叫viewModel的變量
        // binding.setVariable(BR.viewModel, MainViewModel())
        // 以下是一些說(shuō)明:
        // BR就是前文提到的DataBinding資源袍暴,像R文件一樣自動(dòng)生成,記錄所有xml中data標(biāo)簽內(nèi)的變量名稱隶症,有點(diǎn)像控件id的感覺(jué)
        // viewModel來(lái)自布局文件中data標(biāo)簽內(nèi)的variable標(biāo)簽中的name

        // 第二種將數(shù)據(jù)填充到xml文件中的方法(代碼在下面這行),viewModel這個(gè)變量名視你在xml中variable標(biāo)簽中的name而定
        binding.viewModel = MainViewModel()
        // 假如你的name為user,并且class名稱也為User的話(name和class的名稱不一定要相同)
        // 那么代碼就是binding.user = User()

        // java 代碼如下
        // binding.setViewModel(new MainViewModel())
        // binding.setUser(new User())
    }

    override fun onDestroy() {
        super.onDestroy()
        // 在Activity銷毀時(shí)記得解綁政模,以免內(nèi)存泄漏
        binding.unbind()
    }
}

3, 加入DataBinding后蚂会,xml文件的一些新的用法

  1. 數(shù)據(jù)的填充

    可以很明顯的看到览徒,我們?cè)诓季治募淖钔鈱硬皇侨魏尾季謽?biāo)簽,而是layout標(biāo)簽颂龙。之后再引入data標(biāo)簽习蓬,data里面是變量集合,整個(gè)xml文件中只允許有一個(gè)data標(biāo)簽措嵌。data標(biāo)簽中可以包含多個(gè)variable躲叼。name代表變量名稱,type是變量類型企巢。在activity中枫慷,我們新建了一個(gè)binding變量,并且通過(guò)binding變量把MainViewModel實(shí)例化的對(duì)象賦值到xml文件中浪规,這樣我們?cè)趚ml中就可以直接填充到對(duì)應(yīng)控件中或听。通過(guò)@{},我們的控件就可以直接引用到viewModel中的對(duì)應(yīng)的值笋婿。就像:

    android:text="@{viewModel.name}"
    
  2. import標(biāo)簽

    就像java中的import關(guān)鍵字一樣誉裆,可以導(dǎo)入類型,所以我們上面的xml文件中data部份還可以這樣寫:

    <import type="top.cyixlq.test.MainViewModel"/>
    <variable
        name="viewModel"
        type="MainViewModel"/>
    

    我們還注意到缸濒,填充age屬性的時(shí)候足丢,我們是@{String.valueOf(viewModel.age)}粱腻。因?yàn)閍ge是整數(shù)型,我們知道TextView的Text是不可以為整數(shù)型的斩跌,所以我們使用了String這個(gè)類中的方法進(jìn)行了轉(zhuǎn)換绍些。按理說(shuō),String理應(yīng)也需要使用import標(biāo)簽進(jìn)行引入耀鸦,然而我們并沒(méi)有這么做柬批。是的,和Java一樣袖订,java.lang包下的東西是自動(dòng)引入的氮帐。

  3. 三元運(yùn)算符和lambda表達(dá)式以及簡(jiǎn)單運(yùn)算

    我們可以看到,我們填充isMan這個(gè)屬性的時(shí)候使用了三元運(yùn)算符著角,并且使用@string/man和@string/woman作為兩個(gè)可選值揪漩。看起來(lái)是不是很神奇吏口?其實(shí)我們也可以直接這樣寫:

    android:text='@{viewModel.isMan ? "男" : "女"}'
    

    但是值得注意的是奄容,在Windows下,我們這樣寫可能會(huì)報(bào)錯(cuò)产徊。是關(guān)于utf-8的一個(gè)錯(cuò)誤昂勒,具體不太清楚。如果你是java代碼舟铜,在編譯的時(shí)候會(huì)告訴你這個(gè)錯(cuò)誤戈盈。如果是kotlin下,就會(huì)顯示無(wú)法打印這個(gè)錯(cuò)誤log谆刨。所以我還是推薦引用string資源塘娶。

    就像我們?cè)趚ml文件中設(shè)置按鈕的點(diǎn)擊事件一樣,我們可以直接引入lambda表達(dá)式痊夭,從而直接調(diào)用viewModel中的公開(kāi)方法刁岸,是不是覺(jué)得簡(jiǎn)單多了?

    雖然可以直接在xml文件中進(jìn)行運(yùn)算了她我,例如字符串的拼接虹曙,數(shù)字的加減,如下所示:

    android:text="@{String.valueOf(viewModel.age + 1)}"
    android:text='"性別:" + viewModel.isMan ? "男" : "女"'
    

    但是番舆,我不推薦在xml文件中進(jìn)行過(guò)于復(fù)雜的運(yùn)算酝碳,可以在ViewModel類中處理好之后利用函數(shù)返回。如下所示:

    <!-- xml文件中 -->
    android:text="@{viewModel.convertSex()}"
    
    // MainViewModel文件中
    fun convertSex(): String {
        var result = "性別:"
        val sex = if (isMan) "男" else "女"
        return result + sex
    }
    
  4. include標(biāo)簽的一些變化

    我們?cè)陂_(kāi)發(fā)中難免要進(jìn)行布局的復(fù)用恨狈,這就會(huì)用到include標(biāo)簽了疏哗,但是如果我們引入的布局文件中也有variable怎么辦,怎么才能從當(dāng)前布局文件中傳入到include導(dǎo)入的布局中呢拴事?請(qǐng)直接看代碼說(shuō)明沃斤!我們以自己做一個(gè)標(biāo)題欄為例圣蝎。

    1. 首先隱藏我們?cè)械臉?biāo)題欄刃宵,在Activity的onCreate中加入下面的代碼:
      supportActionBar?.hide()
      // java代碼
      // ActionBar actionBar = getSupportActionBar();
      // if (actionBar != null) actionBar.hide()
      
    2. 新建一個(gè)layout_title_bar.xml衡瓶,內(nèi)容如下:
      <layout xmlns:android="http://schemas.android.com/apk/res/android">
          <data>
              <variable
                  name="title"
                  type="String"/>
          </data>
          <RelativeLayout
              android:layout_width="match_parent"
              android:layout_height="?attr/actionBarSize"
              android:background="@color/colorPrimary">
      
              <TextView
                  android:layout_width="wrap_content"
                  android:layout_height="wrap_content"
                  android:layout_centerInParent="true"
                  android:textColor="@android:color/white"
                  android:text="@{title}"/>
      
          </RelativeLayout>
      </layout>
      
    3. 在activity_main.xml中加入以下代碼:
      <variable
          name="text"
          type="String" />
      
      <!-- 這里傳過(guò)去的屬性名稱要與include引入的布局文件中variable的name一樣 -->
      <!-- 同樣,這里可以使用MainViewModel中的某個(gè)字符串屬性作為值傳過(guò)去牲证,不聲明一個(gè)新的variable -->
      <include layout="@layout/layout_titlt_bar"
          title="@{text}"/>
      
    4. 別忘了在Activity中給text賦值:
      binding.text = "測(cè)試"
      

4哮针, 關(guān)于Activity文件中binding變量的一些說(shuō)明

  1. binding變量的類型是ActivityMainBinding,這個(gè)是項(xiàng)目build后自動(dòng)生成的坦袍,根據(jù)布局文件名:activity_main.xml 來(lái)命名的類名稱十厢。也許你也發(fā)現(xiàn)了,它就是布局文件每個(gè)單詞首字母大寫捂齐,然后拼接上Binding蛮放。
  2. 我們前面說(shuō)過(guò),加入DataBinding后我們可以省去一些UI相關(guān)代碼奠宜,比如findviewById包颁。那么具體是怎么操作呢。很簡(jiǎn)單压真,在binding變量賦值后娩嚼,我們直接通過(guò)binding.控件ID就可以直接獲取該控件實(shí)例。例如:binding.button.setOnClickListener(*)
  3. 我們還可以將xml文件中的variable進(jìn)行賦值滴肿。具體見(jiàn)上面Activity代碼及相關(guān)注釋岳悟!

5,數(shù)據(jù)的實(shí)時(shí)更新泼差,雙向綁定

在MainViewModel中和xml布局文件中新添加如下代碼:

// MainViewModel中
fun oneYearLater() {
    age++
    Log.d("MyTAG", "年齡:$age")
}

<!-- 在xml布局文件中 -->
<Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:onClick="@{v -> viewModel.oneYearLater()}"
    android:text="一年后"/>

當(dāng)我們點(diǎn)擊這個(gè)按鈕一年后之后會(huì)執(zhí)行oneYearLater方法贵少,里面的age屬性會(huì)自增。但是堆缘,這樣寫好之后滔灶,我們發(fā)現(xiàn)age變化了,但是視圖上的年齡的文字并沒(méi)有刷新套啤。我們不是說(shuō)加入了DataBinding之后會(huì)自動(dòng)實(shí)時(shí)刷新嗎宽气?別急,如果我們要實(shí)現(xiàn)實(shí)時(shí)刷新的話潜沦,我們要對(duì)MainViewModel進(jìn)行小小的改造萄涯,其中有三種方法:

  1. 就像下面那樣,將對(duì)應(yīng)屬性改成這樣:

    // var age = 15
    var age = ObservableInt(15)
    
    fun oneYearLater() {
        // age++
        val lastAge = age.get()
        age.set(lastAge + 1)
        Log.d("MyTAG", "年齡:$age")
    }
    

    將變量age聲明為可觀察的Int對(duì)象唆鸡。其中涝影,類似ObservableInt的變量類型還有:

    • ObservableBoolean
    • ObservableByte
    • ObservableChar
    • ObservableDouble
    • ObservableLong
      ...此處省略了一些基本數(shù)據(jù)類型

    對(duì)于列表和Map,還有下面這些類型:

    • ObservableList< T >
    • ObservableArrayList< T >
    • ObservableArrayMap<K,V>
    • ObservableMap<K,V>

    那么對(duì)于String或者自定義的類這種非基本數(shù)據(jù)類型争占,那么怎么辦燃逻?DataBinding給我們提供了:ObservableField<T>序目,我們就可以這樣用:

    val name = ObservableField<String>("張三")
    

    對(duì)于序列化,還有這個(gè)數(shù)據(jù)類型:

    ObservableParcelable< T >

  2. 讓類繼承BaseObservable:

    我們先新建一個(gè)ObserveViewModel的類伯襟,讓它繼承BaseObservable:

    class ObserveViewModel : BaseObservable() {
    
        private var firstName = "y"
        private var lastName = "c"
    
        // 這里要加上這個(gè)標(biāo)簽猿涨,在set方法中BR才能找到對(duì)應(yīng)屬性
        @Bindable
        fun getFirstName(): String {
            return firstName
        }
    
        @Bindable
        fun getLastName():String {
            return lastName
        }
    
        fun setFirstName(name:String) {
            this.firstName = name
            notifyPropertyChanged(BR.firstName)
        }
    
        fun setLastName(name:String) {
            this.lastName = name
            notifyPropertyChanged(BR.lastName)
        }
    
        // 改姓的方法
        fun changeLastName() {
            setLastName("薛")
        }
    
    }
    

    在xml布局中進(jìn)行引入,并且將對(duì)應(yīng)屬性值進(jìn)行展示以及設(shè)定按鈕點(diǎn)擊事件:

    <!-- 這段請(qǐng)放在data標(biāo)簽內(nèi) -->
    <variable
        name="observeViewModel"
        type="top.cyixlq.test.ObserveViewModel"/>
    
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@{observeViewModel.firstName}"/>
    
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@{observeViewModel.lastName}"/>
    
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="改姓"
        android:onClick="@{v -> observeViewModel.changeLastName()}"/>
    

    別忘了還要在Activity中進(jìn)行賦值:

    val observeViewModel = ObserveViewModel()
    binding.observeViewModel = observeViewModel
    
  3. 在Activity中進(jìn)行監(jiān)聽(tīng)姆怪。

    在ObserveViewModel類中新添加一個(gè)屬性:

    val age = ObservableInt(17)
    

    在Activity中新加入如下代碼:

    // 給按鈕設(shè)置點(diǎn)擊監(jiān)聽(tīng)事件
    binding.btnAddAge.setOnClickListener {
        val lastAge = observeViewModel.age.get()
        observeViewModel.age.set(lastAge + 1)
    }
    // 監(jiān)聽(tīng)ObserveViewModel中值的變化并進(jìn)行回調(diào)處理
    observeViewModel.age.addOnPropertyChangedCallback(object : Observable.OnPropertyChangedCallback() {
        override fun onPropertyChanged(observable: Observable, i: Int) {
            binding.age.text = observeViewModel.age.get().toString()
        }
    })
    

    在布局文件中新增一個(gè)TextView展示新的屬性叛赚,并添加一個(gè)按鈕改變新的屬性值:

    <TextView
        android:id="@+id/age"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="年齡"/>
    
    <Button
        android:id="@+id/btn_add_age"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="加一歲"/>
    

看過(guò)上面三種方法后,是不是覺(jué)得第一種方法最簡(jiǎn)單稽揭?是的俺附,我個(gè)人也比較推崇第一種方法,簡(jiǎn)單粗暴溪掀,但是并不意味著其他方法就用不到了事镣,我們還是應(yīng)該根據(jù)業(yè)務(wù)需求使用不同的方法靈活變通!

了解Vue的同學(xué)知道揪胃,當(dāng)我使用:value={{text}}的時(shí)候璃哟,就可以實(shí)現(xiàn)數(shù)據(jù)視圖雙向綁定。即輸入框中的內(nèi)容是什么只嚣,對(duì)應(yīng)的屬性值就是輸入框中的內(nèi)容沮稚。那么,DataBding也可以做到嗎册舞?答案是當(dāng)然可以的蕴掏。首先我們?cè)贛ainViewModel中新添加一個(gè)text屬性:

val text = ObservableField<String>("")

然后在activity_main.xml布局文件中多加一個(gè)EditText和一個(gè)TextView:

<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@{viewModel.text}"/>

<EditText
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="@={viewModel.text}"/>

這樣做完之后,我們?cè)谳斎肟蛑休斎胧裁吹骶ǎ覀冊(cè)赥extView上面看到的就是什么盛杰。這樣就實(shí)現(xiàn)了雙向綁定。我們不然發(fā)現(xiàn)藐石,我們實(shí)現(xiàn)雙向綁定其實(shí)就是多加了一個(gè)“ = ”即供!

OK,這一小章節(jié)我們就先到這里了于微,下一章節(jié)我們就介紹一下DataBinding的一些常用注解逗嫡!另外本章節(jié)中出現(xiàn)的問(wèn)題還望大家留言指正,畢竟還是在MVVM探索道路中株依!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末驱证,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子恋腕,更是在濱河造成了極大的恐慌抹锄,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,884評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異伙单,居然都是意外死亡获高,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門吻育,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)念秧,“玉大人,你說(shuō)我怎么就攤上這事扫沼〕龅” “怎么了庄吼?”我有些...
    開(kāi)封第一講書(shū)人閱讀 158,369評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵缎除,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我总寻,道長(zhǎng)器罐,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,799評(píng)論 1 285
  • 正文 為了忘掉前任渐行,我火速辦了婚禮轰坊,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘祟印。我一直安慰自己肴沫,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,910評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布蕴忆。 她就那樣靜靜地躺著颤芬,像睡著了一般。 火紅的嫁衣襯著肌膚如雪套鹅。 梳的紋絲不亂的頭發(fā)上站蝠,一...
    開(kāi)封第一講書(shū)人閱讀 50,096評(píng)論 1 291
  • 那天,我揣著相機(jī)與錄音卓鹿,去河邊找鬼菱魔。 笑死,一個(gè)胖子當(dāng)著我的面吹牛吟孙,可吹牛的內(nèi)容都是我干的澜倦。 我是一名探鬼主播,決...
    沈念sama閱讀 39,159評(píng)論 3 411
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼杰妓,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼藻治!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起稚失,我...
    開(kāi)封第一講書(shū)人閱讀 37,917評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤栋艳,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后句各,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,360評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,673評(píng)論 2 327
  • 正文 我和宋清朗相戀三年特占,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了担扑。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,814評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡集侯,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情孙技,我是刑警寧澤,帶...
    沈念sama閱讀 34,509評(píng)論 4 334
  • 正文 年R本政府宣布排作,位于F島的核電站牵啦,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏妄痪。R本人自食惡果不足惜哈雏,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,156評(píng)論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望衫生。 院中可真熱鬧裳瘪,春花似錦、人聲如沸罪针。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)泪酱。三九已至派殷,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間西篓,已是汗流浹背愈腾。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,123評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留岂津,地道東北人虱黄。 一個(gè)月前我還...
    沈念sama閱讀 46,641評(píng)論 2 362
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像吮成,于是被迫代替她去往敵國(guó)和親橱乱。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,728評(píng)論 2 351