Android JetPack系列之——DataBinding

在傳統(tǒng)的Android應(yīng)用開發(fā)中策添,布局文件通常只負(fù)責(zé)應(yīng)用界面的布局工作瘦癌,如果需要實現(xiàn)頁面交互就需要調(diào)用setContentView()將Activity、fragment和XML布局文件關(guān)聯(lián)起來。然后通過控件的id找到控件前硫,接著在頁面中通過代碼對控件進(jìn)行邏輯處理临梗。在這種傳統(tǒng)的開發(fā)方式中涡扼,頁面承擔(dān)了大部分的工作量,大量的邏輯處理需要在Activity盟庞、Fragment中進(jìn)行處理吃沪,因此頁面顯得臃腫不堪,維護(hù)起來也很困難什猖,為了減輕頁面的工作量票彪,Google提出了DataBinding(視圖綁定)。

一不狮、DataBinding的優(yōu)點降铸?

(1)解耦。將部分原屬于Activity/Fragment去實現(xiàn)的功能交由Model實體類去實現(xiàn)荤傲,從而實現(xiàn)了部分功能代碼的分離垮耳,既保證了Activity/Fragment過于臃腫,也便于后期代碼的維護(hù)和擴(kuò)展。
(2)避免重復(fù)無效代碼终佛。不再需要findViewById操作(其實這條不是那么重要俊嗽,因為采用kotlin編碼的話本身就沒有findViewById)。

總結(jié):Databinding的作用就是在XML文件里面實現(xiàn)數(shù)據(jù)的部分綁定從而避免將數(shù)據(jù)的全部展示交由Controller層去實現(xiàn)從而達(dá)到代碼的易于擴(kuò)展和后期的維護(hù)目的铃彰。

二绍豁、DataBinding的基本使用

先來了解一下DataBinding常用的幾個類:

  • DataBindingUtil:在Activity/Fragment中獲取相關(guān)的Binding對象。
  • BaseObservable:Bean可以繼承該抽象類牙捉,實現(xiàn)可觀察的模式竹揍,在set屬性的時候調(diào)用notifyPropertyChanged方法,喚起刷新操作邪铲,也可以調(diào)用notifyChange方法全部刷新芬位。
  • Observable:Bean可以實現(xiàn)該接口,實現(xiàn)可觀察的模式带到,在set屬性的時候調(diào)用notifyPropertyChanged方法昧碉,喚起刷新操作,也可以調(diào)用notifyChange方法全部刷新揽惹。
  • ObservableFloat:這不是一個類被饿,而是一類類的代表,如ObservableShort搪搏、ObservableParcelable等等狭握,可觀察的屬性,通過get和set方法操作相關(guān)的值疯溺。
  • BaseObservableField<>:和上述類似论颅,泛型可以傳入String等類型,比上述定義的基類型更加自由喝检。

1.添加依賴

android {
    ...
    dataBinding {
        enabled = true
    }
}

2.修改布局文件
<layout>為頭嗅辣,以</layout>為尾。

<layout>
    <data>
        <variable
            name="login"
            type="com.jack.androidjetpack.login.Login" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingBottom="@dimen/activity_vertical_margin">
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

3.點擊Build->Rebuild Project生成對應(yīng)的Binding

4.使用DataBindingUtil類來進(jìn)行視圖的綁定
Activity的處理方式

class LoginActivity : AppCompatActivity() {

    var binding: ActivityLoginBinding? = null
    var login: Login? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this, R.layout.activity_login)
        login = Login("jack", "123456")
        binding?.setLogin(login)
    }
}

Fragment的處理方式

class PhoneCodeFragment : Fragment() {

    private var param1: String? = null
    private var param2: String? = null
    private var binding: FragmentPhoneCodeBinding? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        arguments?.let {
            param1 = it.getString(ARG_PARAM1)
            param2 = it.getString(ARG_PARAM2)
        }
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        binding = FragmentPhoneCodeBinding.inflate(inflater, container, false)
        return binding?.root
    }

    companion object {
        @JvmStatic
        fun newInstance(param1: String, param2: String) =
            PhoneCodeFragment().apply {
                arguments = Bundle().apply {
                    putString(ARG_PARAM1, param1)
                    putString(ARG_PARAM2, param2)
                }
            }
    }
}

5.在layout布局中添加data標(biāo)簽
經(jīng)過前面的幾個步驟挠说,我們已經(jīng)將Databinding和我們的XML文件綁定起來了澡谭,現(xiàn)在你點擊Databinding會發(fā)現(xiàn)直接可以跳轉(zhuǎn)到對應(yīng)的XML文件里面去了,現(xiàn)在我們就來看看如何給我們的XML文件里面的View設(shè)置值损俭。

在XML文件的layout標(biāo)簽下蛙奖,創(chuàng)建data標(biāo)簽,在data標(biāo)簽中再創(chuàng)建variable標(biāo)簽杆兵,variable標(biāo)簽主要用到的就是name屬性和type屬性雁仲,類似于Java語言聲明變量時,需要為該變量指定類型和名稱琐脏。新建一個名為Login的數(shù)據(jù)類攒砖。

data class Login(var userName: String, var password: String):BaseObservable()

然后在布局的 data 標(biāo)簽里聲明要使用到的變量名缸兔、類的全路徑等信息,如下所示:

<layout>

    <data>

        <variable
            name="login"
            type="com.jack.androidjetpack.login.Login" />

    </data>
    ...
</layout>

如果 Login有多處用到吹艇,也可以直接將之 import 進(jìn)來惰蜜,這樣就不用每次都指明整個包名路徑了,而 java.lang.* 包中的類會被自動導(dǎo)入受神,所以可以直接使用抛猖。

<layout>

    <data>

        <import type="com.jack.androidjetpack.login.Login" />

        <variable
            name="login"
            type="Login" />

    </data>
    ...
</layout>

在XML文件中聲明好variable屬性后,接下來就可以在XML使用它了鼻听。使用variable屬性時需要使用到布局表達(dá)式: @{ }财著。可以在布局表達(dá)式@{ }中獲取傳入variable對象的值撑碴,如下所示:

 <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingBottom="@dimen/activity_vertical_margin">

        <EditText
            android:id="@+id/username"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginStart="24dp"
            android:layout_marginTop="96dp"
            android:layout_marginEnd="24dp"
            android:hint="@string/prompt_name"
            android:inputType="textEmailAddress"
            android:selectAllOnFocus="true"
            android:text="@{login.userName}"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <EditText
            android:id="@+id/password"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginStart="24dp"
            android:layout_marginTop="8dp"
            android:layout_marginEnd="24dp"
            android:hint="@string/prompt_password"
            android:imeActionLabel="@string/action_sign_in_short"
            android:imeOptions="actionDone"
            android:inputType="textPassword"
            android:selectAllOnFocus="true"
            android:text="@{login.password}"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/username" />
    
    </androidx.constraintlayout.widget.ConstraintLayout>

最后在我們的Controller層將我們的datamodel相關(guān)聯(lián)撑教。

    var binding: ActivityLoginBinding? = null
    var login: Login? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this, R.layout.activity_login)
        login = Login("jack", "123456")
        binding?.setLogin(login)
    }

到這里,一個最基礎(chǔ)的DataBinding的例子就結(jié)束了醉拓,接下來我們繼續(xù)往下看驮履。

三、DataBinding的進(jìn)階

進(jìn)階1:給控件添加響應(yīng)事件
方式一:直接在Controller層通過原來的方式添加

binding?.login?.setOnClickListener {
          
}

方式二:創(chuàng)建一個工具類廉嚼,在類中定義響應(yīng)的點擊事件
第一步:創(chuàng)建點擊的工具類

/**
 * @author: zhoufan
 * @date:   2021/8/30 11:14
 */
class ButtonClickListener {

    fun click(view: View) {
        Log.e("click","響應(yīng)登錄的點擊事件")
    }
}

第二步:在XML文件中添加工具類

<variable
      name="btnHandler"
      type="com.jack.androidjetpack.login.ButtonClickListener" />

第三步:在XML文件中添加響應(yīng)事件

<Button
     android:id="@+id/login"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:onClick="@{btnHandler::click}"
     android:text="@string/action_sign_in"
/>

第四步:在Controller里面進(jìn)行關(guān)聯(lián)

class LoginActivity : AppCompatActivity() {

    var binding: ActivityLoginBinding? = null
    var login: Login? = null
    var clickListener:ButtonClickListener?=null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this, R.layout.activity_login)
        login = Login("jack", "123456")
        clickListener= ButtonClickListener()
        binding?.setLogin(login)
        // 這一步必須要,否則點擊沒反應(yīng)
        binding?.btnHandler = clickListener
    }
}

進(jìn)階2:BindingAdapter
使用DataBinding庫時倒戏,DataBinding會針對控件屬性生成對應(yīng)的XXXBindingAdapter類怠噪,如TextViewBindingAdapter類,其對TextView的每個可以使用DataBinding的屬性都生成了對應(yīng)的方法杜跷,而且每個方法都使用了@BindingAdapter注解傍念,注解中的參數(shù)就是對應(yīng)View的屬性。

自定義BindingAdapter
編寫一個處理圖片的自定義BindingAdapter類葛闷。然后定義一個靜態(tài)方法憋槐,主要用于添加 BindingAdapter 注解,注解值是 ImageView 控件自定義的屬性名淑趾,如下所示阳仔。

class ImageBindingAdapter {

    companion object {
        @BindingAdapter("url")
        @JvmStatic
        fun loadImage(view: ImageView?, url: String?) {
            var realValue: String? = null
            if (url.equals("null")) {
                realValue = "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fattach.bbs.miui.com%2Fforum%2F201412%2F27%2F111335whdlgodddosl6swq.jpg&refer=http%3A%2F%2Fattach.bbs.miui.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1632887161&t=f6a13d2953ea7be364a111344b72a654"
            }
            if (!TextUtils.isEmpty(realValue)) {
                Glide.with(view!!)
                    .load(realValue)
                    .into(view)
            }
        }
    }
}

在XML文件里面直接引用

<ImageView
      android:layout_width="300dp"
      android:layout_height="200dp"
      android:layout_below="@+id/login"
      android:layout_marginTop="16dp"
      app:url="@{`null`}" />

有時候,我們需要自定義多個屬性扣泊,那如何處理呢近范?和一個參數(shù)一樣,我們只需要使用BindingAdapter添加參數(shù)即可延蟹,如下所示:

class ImageBindingAdapter {

    companion object {
        @BindingAdapter(value = ["url", "placeholder", "error"])
        @JvmStatic
        fun loadImage(view: ImageView?, url: String?, placeholder: Drawable?, error: Drawable?) {
            var realValue: String? = null
            if (url.equals("null")) {
                realValue =
                    "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fattach.bbs.miui.com%2Fforum%2F201412%2F27%2F111335whdlgodddosl6swq.jpg&refer=http%3A%2F%2Fattach.bbs.miui.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1632887161&t=f6a13d2953ea7be364a111344b72a654"
            }
            if (!TextUtils.isEmpty(realValue)) {
                val options = RequestOptions()
                options.placeholder(placeholder)
                options.error(error)
                Glide.with(view!!)
                    .load(realValue)
                    .apply(options)
                    .into(view)
            }
        }
    }
}

對應(yīng)的XML文件為:

<ImageView
       android:layout_width="300dp"
       android:layout_height="200dp"
       android:layout_below="@+id/login"
       android:layout_marginTop="16dp"
       app:placeholder="@{ContextCompat.getDrawable(context, R.mipmap.ic_launcher)}"
       app:error="@{ContextCompat.getDrawable(context, R.mipmap.ic_launcher)}"
       app:url="@{`null`}" />

別忘記導(dǎo)入對應(yīng)的依賴

<import type="androidx.core.content.ContextCompat" />
<import type="com.jack.androidjetpack.R"/>

進(jìn)階3:雙向綁定
DataBinding的本身是對View層狀態(tài)的一種觀察者模式的實現(xiàn)评矩,通過讓View與ViewModel層可觀察的對象進(jìn)行綁定,當(dāng)ViewModel層數(shù)據(jù)發(fā)生改變時阱飘,View層也會自動進(jìn)行UI的更新斥杜,這種場景稱之為單向綁定虱颗。

但是在實際的開發(fā)過程中,單向綁定并不能滿足所有的需求蔗喂。例如有下面的場景:如果布局中有一個EditText忘渔,當(dāng)用戶在輸入框中輸入內(nèi)容時,我們希望對應(yīng)的Model類能夠?qū)崟r更新弱恒,這就需要雙向綁定辨萍,DataBinding同樣支持這樣的能力。

實現(xiàn)雙向綁定需要用到ObservableField類返弹,它能夠?qū)⑵胀ǖ臄?shù)據(jù)對象包裝成一個可觀察的數(shù)據(jù)對象锈玉,數(shù)據(jù)類型可以是基本數(shù)據(jù)類型、變量义起、集合拉背,也可以是自定義類型。

第一步:修改我們的實體類

class Login : BaseObservable() {

    @get:Bindable
    var userName: String? = null
        set(userName) {
            field = userName
            notifyPropertyChanged(BR.userName)
        }

    @get:Bindable
    var password: String? = null
        set(password) {
            field = password
             notifyPropertyChanged(BR.userName)
        }
}

第二步:修改我們的實體類

  <EditText
            android:id="@+id/username"
            android:layout_width="match_parent"
            android:layout_height="40dp"
            android:layout_marginStart="24dp"
            android:layout_marginTop="96dp"
            android:layout_marginEnd="24dp"
            android:hint="@string/prompt_name"
            android:inputType="textEmailAddress"
            android:selectAllOnFocus="true"
            android:text="@={login.userName}" />

        <EditText
            android:id="@+id/password"
            android:layout_width="match_parent"
            android:layout_height="40dp"
            android:layout_below="@+id/username"
            android:layout_marginStart="24dp"
            android:layout_marginTop="8dp"
            android:layout_marginEnd="24dp"
            android:hint="@string/prompt_password"
            android:imeActionLabel="@string/action_sign_in_short"
            android:imeOptions="actionDone"
            android:inputType="textPassword"
            android:selectAllOnFocus="true"
            android:text="@={login.password}" />

將我們原來的@{login.userName}換成@={login.userName}就可以了默终。

四椅棺、DataBinding的實戰(zhàn)

在我們的實際開發(fā)過程中,RecyclerView算是使用非常頻繁的齐蔽,接下來我們就看看如何使用DataBinding對RecyclerView進(jìn)行處理两疚。
第一步:定義我們的實體類

class UserModel: BaseObservable() {

    @get:Bindable
    var name: String? = null
        set(name) {
            field = name
            notifyPropertyChanged(BR.name)
        }

    @get:Bindable
    var address: String? = null
        set(address) {
            field = address
            notifyPropertyChanged(BR.address)
        }

    @get:Bindable
    var age: String? = null
        set(age) {
            field = age
            notifyPropertyChanged(BR.age)
        }
}

第二步:創(chuàng)建我們的適配器的布局

<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>
        <variable
            name="user"
            type="com.jack.androidjetpack.list.UserModel" />
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="10dp"
        android:orientation="horizontal">

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

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

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{user.age}" />
    </LinearLayout>
</layout>?

第三步:創(chuàng)建我們的適配器

class UserAdapter : RecyclerView.Adapter<UserAdapter.ViewHolder>() {

    var dataList: MutableList<UserModel> = mutableListOf()
        set(value) {
            field = value
            notifyDataSetChanged()
        }


    inner class ViewHolder(binding: ViewDataBinding) : RecyclerView.ViewHolder(binding.root) {

        var adapterBinding: AdapterListBinding? = null

        init {
            adapterBinding = binding as AdapterListBinding
        }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val binding: ViewDataBinding = DataBindingUtil.inflate(
            LayoutInflater.from(parent.context),
            R.layout.adapter_list,
            parent,
            false
        )
        return ViewHolder(binding)
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        val userModel = dataList[position]
        holder.adapterBinding?.user = userModel
    }

    override fun getItemCount() = dataList.size

}

第四步:在Activity里面完成功能

class ListActivity : AppCompatActivity() {

    private var userModelList: MutableList<UserModel>? = mutableListOf()
    private var activityListBinding: ActivityListBinding? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        activityListBinding = DataBindingUtil.setContentView(this, R.layout.activity_list)
        initData()
        initRecyclerView()
    }

    private fun initData() {
        for (i in 0..9) {
            val userModel = UserModel()
            userModel.name = "jack" + 1
            userModel.address = "beijing$i"
            userModel.age = "age$i"
            userModelList?.add(userModel)
        }
    }

    private fun initRecyclerView() {
        val layoutManager = LinearLayoutManager(this)
        activityListBinding?.recyclerView?.layoutManager = layoutManager
        val adapter = UserAdapter()
        activityListBinding?.recyclerView?.adapter = adapter
        adapter.dataList = userModelList!!
    }
}

好了,關(guān)于DataBinding的內(nèi)容就介紹到這里了含滴,當(dāng)然诱渤,實際開發(fā)過程中DataBinding使用的遠(yuǎn)遠(yuǎn)不止這些,需要我們平時多總結(jié)谈况,多思考才能有更多的收獲勺美。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市碑韵,隨后出現(xiàn)的幾起案子赡茸,更是在濱河造成了極大的恐慌,老刑警劉巖祝闻,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件占卧,死亡現(xiàn)場離奇詭異,居然都是意外死亡治筒,警方通過查閱死者的電腦和手機(jī)屉栓,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來耸袜,“玉大人友多,你說我怎么就攤上這事〉炭颍” “怎么了域滥?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵纵柿,是天一觀的道長。 經(jīng)常有香客問我启绰,道長昂儒,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任委可,我火速辦了婚禮渊跋,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘着倾。我一直安慰自己拾酝,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布卡者。 她就那樣靜靜地躺著蒿囤,像睡著了一般。 火紅的嫁衣襯著肌膚如雪崇决。 梳的紋絲不亂的頭發(fā)上材诽,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天,我揣著相機(jī)與錄音恒傻,去河邊找鬼脸侥。 笑死,一個胖子當(dāng)著我的面吹牛盈厘,可吹牛的內(nèi)容都是我干的湿痢。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼扑庞,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了拒逮?” 一聲冷哼從身側(cè)響起罐氨,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎滩援,沒想到半個月后栅隐,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡玩徊,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年租悄,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片恩袱。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡泣棋,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出畔塔,到底是詐尸還是另有隱情潭辈,我是刑警寧澤鸯屿,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站把敢,受9級特大地震影響寄摆,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜修赞,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一婶恼、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧柏副,春花似錦勾邦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至锨推,卻和暖如春铅歼,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背换可。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工椎椰, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人沾鳄。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓慨飘,卻偏偏與公主長得像,于是被迫代替她去往敵國和親译荞。 傳聞我的和親對象是個殘疾皇子瓤的,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內(nèi)容