“終于懂了“系列:Jetpack AAC完整解析(五)DataBinding 重新認(rèn)知窘疮!

Jetpack AAC 系列文章:

“終于懂了“系列:Jetpack AAC完整解析(一)Lifecycle 完全掌握!

“終于懂了“系列:Jetpack AAC完整解析(二)LiveData 完全掌握郑叠!

“終于懂了“系列:Jetpack AAC完整解析(三)ViewModel 完全掌握秋泳!

“終于懂了“系列:Jetpack AAC完整解析(四)MVVM 架構(gòu)探索!

“終于懂了“系列:Jetpack AAC完整解析(五)DataBinding 架構(gòu)完善倡缠!

前面四篇介紹了Jetpack 架構(gòu)組件中的 基礎(chǔ)組件 以及它們的綜合應(yīng)用:Jetpack MVVM 架構(gòu)模式哨免,到這里已經(jīng)基本滿足標(biāo)準(zhǔn)化開發(fā)了。但 Jetpack 架構(gòu)組件 除了 Lifecycle毡琉、LivaData铁瞒、ViewModel妙色,還有:

  • WorkManager桅滋,用于管理后臺(tái)工作的任務(wù),即使應(yīng)用退出或重啟時(shí)身辨。
  • Paging丐谋,分頁庫,按需加載部分?jǐn)?shù)據(jù)煌珊。
  • Startup号俐,用于App啟動(dòng)速度優(yōu)化的庫,但只適用于庫開發(fā)者定庵, 郭霖這篇有詳細(xì)介紹吏饿。
  • DataStore,用于替換SharedPreferences蔬浙,目前還處于Alpha階段猪落。
  • DataBinding,將布局中的界面組件直接綁定到數(shù)據(jù)源畴博,提供雙向綁定笨忌,及高級綁定適配能力。
  • ViewBinding俱病,用于替代findViewById官疲,而DataBinding也包含ViewBinding的能力袱结。
  • Room,實(shí)現(xiàn)本地存儲(chǔ) 數(shù)據(jù)庫管理途凫,支持LiveData垢夹。

目前,就學(xué)習(xí)使用的必要性和庫的功能性 來說维费,WorkManager棚饵、Paging、Startup都是非必須的掩完,DataStore還未正式發(fā)布噪漾,ViewBinding的能力也包含在DataBinding中。Room且蓬,實(shí)際 功能和性能 同GreenDAO類似欣硼,有個(gè)好處是支持LivaData,但已使用GreenDao的項(xiàng)目恶阴,也不必切換為Room了诈胜。

DataBinding是比較有爭議的一個(gè)庫,這也是本篇的重點(diǎn)冯事,相信會(huì)帶你 重新認(rèn)識(shí) 被誤解的 DataBinding焦匈。

一、重新認(rèn)知 DataBinding

DataBinding的使用方法昵仅,參考官方文檔就可以缓熟,介紹地很詳細(xì)了,這里就不再搬運(yùn)摔笤。(另外還找到一個(gè)慕課網(wǎng)的視頻很不錯(cuò):入門篇够滑、高級篇

1.1 DataBinding 的本質(zhì)

應(yīng)該不少人和我以前一樣,對 DataBinding 的認(rèn)知就是 在xml中寫邏輯

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text='@{!isFemale? user.name + ":男士": user.name + ":女士"}'/>

看到 xml 中 使用三元表達(dá)式 來計(jì)算view需要的值吕世,然后就認(rèn)為:“ DataBinding 不好用彰触!在xml中寫表達(dá)式邏輯,出錯(cuò)了debug不了啊命辖,邏輯寫在xml里面的話 xml 就承擔(dān)了 Presenter/ViewModel 的職責(zé) 變得混亂了啊况毅。”

如果是把邏輯寫在xml中尔艇,確實(shí)如是:xml中是不能調(diào)試的尔许、職責(zé)上確實(shí)是混亂了。

但漓帚,這就是 DataBinding 的本質(zhì)了嗎母债?

1.1.1 DataBinding 以前

在 DataBinding 出現(xiàn)以前,想要改變視圖 就要引用該視圖:

        TextView textView = findViewById(R.id.sample_text);
        if (textView != null && viewModel != null) {
            textView.setText(viewModel.getUserName());
        }

而要引用該視圖就要先判空,textView 和 viewModel 都不能為空毡们。textView為啥要判空呢迅皇?一種情況是 R.id.sample_text是定義在在其他頁面中;一種情況是存在控件存在差異的 橫衙熔、豎 兩種布局登颓,如橫屏存在此 textView 控件僧叉,而豎屏沒有栋荸,那么就需要對其做判空處理。

App內(nèi)頁面和控件數(shù)量繁多簿姨,一個(gè)控件可能會(huì)多處調(diào)用痢甘,這就會(huì)有出現(xiàn)空指針的可能喇嘱,那如何完全避免呢?

1.1.2 數(shù)據(jù)綁定

DataBinding塞栅,含義是 數(shù)據(jù)綁定者铜,即 布局中的控件可觀察的數(shù)據(jù) 進(jìn)行綁定。

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

布局中這個(gè)TextView是實(shí)實(shí)在在 存在的放椰,就不需要判空了作烟。而user是否為空 DataBinding也會(huì)自動(dòng)處理:在表達(dá)式 @{user.name} 中,如果 user 為 Null砾医,則為 user.name 分配默認(rèn)值 null拿撩。

并且,當(dāng)該 user.name 被 set 新值時(shí)如蚜,被綁定了該數(shù)據(jù)的控件即可獲得通知和刷新压恒。換言之,在使用 DataBinding 后怖亭,唯一的改變是涎显,你無需手動(dòng)調(diào)用視圖來 set 新狀態(tài),你只需 set 數(shù)據(jù)本身兴猩。

所以,DataBinding 并非是 將 UI 邏輯搬到 XML 中寫 導(dǎo)致而難以調(diào)試 早歇,只負(fù)責(zé)綁定數(shù)據(jù)倾芝, UI 控件 與 其需要的 終態(tài)數(shù)據(jù) 進(jìn)行綁定。 終態(tài)數(shù)據(jù)是指 UI 控件 直接需要的數(shù)據(jù)(UI數(shù)據(jù))箭跳,string值晨另、int值等,而不是一段邏輯(不然就叫 LogicBinding了 谱姓,雖然DataBinding支持邏輯表達(dá)式)借尿。

明確了 DataBinding 的 職責(zé)邊界后 應(yīng)該知道了:原本的邏輯代碼 該怎么寫還是怎么寫,只不過不再需要 textView.setText(user.name),而是直接 user.setName()路翻。

所以 DataBinding 的本質(zhì)就是 終態(tài)數(shù)據(jù) 與 UI控件 的綁定狈癞,具有以下優(yōu)勢

  1. 無需多處調(diào)用控件,原本調(diào)用的地方只需要set數(shù)據(jù)即可
  2. 1的延伸茂契,無需手動(dòng)判空
  3. 1的延伸蝶桶,完全不用寫模板代碼 findViewById
  4. 并且,引入DataBinding后掉冶,原本的 UI 邏輯無需改動(dòng)真竖,只需設(shè)置終態(tài)數(shù)據(jù)

上篇提到過 Jetpack MVVM 架構(gòu)本質(zhì)是數(shù)據(jù)驅(qū)動(dòng),這就是說厌小,控件的狀態(tài)及數(shù)據(jù)是 被分離到 ViewModel 中管理恢共,并且 ViewModel 這一層只需負(fù)責(zé)狀態(tài)數(shù)據(jù)本身的變化,至于該數(shù)據(jù)在布局中是 被哪些視圖綁定璧亚、有沒有視圖來綁定旁振、以及怎么綁定,ViewModel 是不用關(guān)心的涨岁。

那控件是如何做到被通知且更新狀態(tài)的呢拐袜?

DataBinding 是通過 觀察者模式 來管理控件刷新狀態(tài)。當(dāng)狀態(tài)數(shù)據(jù)變化時(shí)梢薪,只需手動(dòng)地完成 setValue蹬铺,這將通知 DataBinding 去刷新 該數(shù)據(jù) 綁定的控件。

而秉撇,文章開頭提到的把邏輯放入xml中的寫法甜攀,是不建議的。數(shù)據(jù)值應(yīng) 直接反映UI控件需要的結(jié)果琐馆,而不是作為邏輯條件放在 xml 中规阀。所以,DataBinding 不負(fù)責(zé) UI 邏輯瘦麸,邏輯原本在哪里寫谁撼,現(xiàn)在還是在哪里寫,只不過滋饲,原本調(diào)用控件實(shí)例去刷新狀態(tài)的方式厉碟,現(xiàn)在改成了數(shù)據(jù)驅(qū)動(dòng)。 這就是DataBinding 的核心目標(biāo)屠缭。

1.2 例子 - 綁定列表數(shù)據(jù)

來舉個(gè)例子進(jìn)行說明:在頁面中展示用戶信息(User)列表箍鼓,同時(shí)還有兩個(gè)按鈕用于添加、移除用戶:

<?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="clickPresenter"
            type="com.hfy.demo01.module.jetpack.databinding.ListActivity.ClickPresenter" />
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context=".module.jetpack.databinding.ListActivity">
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="添加user"
            android:onClick="@{clickPresenter::addUser}"/>
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="移除user"
            android:onClick="@{clickPresenter::removeUser}"/>

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/rv_user_list"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>
    </LinearLayout>
</layout>

我們知道呵曹,RecyclerView的所展示的列表數(shù)據(jù)款咖, 是通過Adapter 對每一項(xiàng)數(shù)據(jù) 分別進(jìn)行設(shè)置的何暮,也就是說User是綁定到 Item的xml中:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable
            name="user"
            type="com.hfy.demo01.module.jetpack.databinding.bean.User" />
    </data>
    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="50dp">
        <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.level}"/>
    </LinearLayout>
</layout>

我們看下,在Activity中是如何處理的:

public class ListActivity extends AppCompatActivity {
  
    private ActivityListBinding mViewDataBinding;
    private static UserListAdapter mAdapter;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mViewDataBinding = DataBindingUtil.setContentView(this, R.layout.activity_list);

        mViewDataBinding.rvUserList.setLayoutManager(new LinearLayoutManager(this, RecyclerView.VERTICAL, false));
        mAdapter = new UserListAdapter();
        mAdapter.setNewInstance(getUserList());
        mViewDataBinding.rvUserList.setAdapter(mAdapter);

        mViewDataBinding.setClickPresenter(new ClickPresenter());
    }

  //這里是假裝 調(diào)用ViewModel能力 獲取用戶數(shù)據(jù)
    private List<User> getUserList() {
        List<User> list = new ArrayList<>();
        list.add(new User("小明","Lv1"));
        list.add(new User("小紅","Lv2"));
        list.add(new User("小q","Lv3"));
        list.add(new User("小a","Lv4"));
        return list;
    }
  
    //點(diǎn)擊監(jiān)聽處理
    public class ClickPresenter {
        public void addUser(View view) {
            Toast.makeText(ListActivity.this, "addUser", Toast.LENGTH_SHORT).show();
            mAdapter.addData(new User("小z","Lv5"));
        }
        public void removeUser(View view) {
            Toast.makeText(ListActivity.this, "removeUser", Toast.LENGTH_SHORT).show();
            mAdapter.remove(0);
        }
    }

    private static class UserListAdapter extends BaseQuickAdapter<User, UserItemViewHolder> {
        public UserListAdapter() {
            super(R.layout.item_user);
        }

        @Override
        protected void convert(@NonNull UserItemViewHolder holder, User user) {
            // 精髓所在1铐殃,不需要去一個(gè)個(gè)setText等等
            holder.getItemUserBinding().setUser(user);
            holder.getItemUserBinding().executePendingBindings();

            //當(dāng)獲取的DataBinding不是具體類時(shí)海洼,只是ViewDataBinding,那就要使用setVariable了
//            holder.getViewDataBinding().setVariable(BR.user, user);
//            holder.getViewDataBinding().executePendingBindings();
        }
    }

    private static class UserItemViewHolder extends BaseViewHolder {

        // 精髓所在2背稼,只需要持有 binding即可贰军,不用去findViewById
        private final ItemUserBinding binding;
//        private final ViewDataBinding binding2;

        public UserItemViewHolder(View view) {
            super(view);
            binding = DataBindingUtil.bind(view);
//            binding2 = DataBindingUtil.bind(view);
        }

        public ItemUserBinding getItemUserBinding() {
            return binding;
        }

//        public ViewDataBinding getViewDataBinding() {
//            return binding2;
//        }
    }

}

RecyclerView的初始化、調(diào)用ViewModel對數(shù)據(jù)的獲取蟹肘,這些處理及邏輯 和之前一毛一樣词疼,不同點(diǎn)在于 Item數(shù)據(jù)的展示:

  1. 在UserItemViewHolder中,不用去findViewById了帘腹,而是直接DataBindingUtil.bind(view)贰盗,ViewHolder只要Hold住 binding就可以了,之前是Hold住所有的view阳欲。
  2. 在UserListAdapter中舵盈,設(shè)置數(shù)據(jù)是,也只是使用 binding 去 setUser(user)即可球化。

二秽晚、自定義屬性 - BindingAdapter

DataBinding 還有個(gè)強(qiáng)大功能:能為控件提供自定義屬性的 BindingAdapter!

不懂筒愚?我們來看個(gè)例子赴蝇。

        <ImageView
            android:layout_width="100dp"
            android:layout_height="100dp"
            app:imageUrl="@{user.avatar}"
            app:placeHolder="@{@drawable/dog}"/>

其中的 app:imageUrl 、app:placeHolder 分別與 user.avatar巢掺、@drawable/dog 綁定了句伶。 但我們知道ImageView本身是沒有這兩個(gè)屬性的,并且我們也并不是 繼承 ImageView 的自定義View陆淀,那為啥可以這樣使用呢考余? 再來看:

    @BindingAdapter({"app:imageUrl", "app:placeHolder"})
    public static void loadImageFromUri(ImageView imageView, String imageUri, Drawable placeHolder){
        Glide.with(imageView.getContext())
                .load(imageUri)
                .placeholder(placeHolder)
                .into(imageView);
    }

在隨便某個(gè)類中添加 public static 方法(方法名隨意),增加注解@BindingAdapter轧苫,并且注明對應(yīng)的"app:imageUrl", "app:placeHolder"楚堤,然后方法參數(shù)是 控件類型 及 這兩個(gè)屬性對應(yīng) 值。 然后在方法中寫邏輯即可浸剩,這里就是使用Glide加載用戶頭像钾军,其中placeHolder是占位圖。

這樣就完成了 圖片的加載了绢要!

使用確實(shí)相當(dāng)簡潔,相當(dāng)于 直接自定義屬性拗小。你可以自定義 任何你想要的屬性重罪。

通常我們可以用 @BindingAdapter 方式,在模塊 內(nèi)部 來做一些公用邏輯。例如這個(gè)圖片加載剿配,@BindingAdapter注解的方法 只要寫一次搅幅,那么 所有用到 ImageView 加載圖片的地方 xml中都可以 直接使用屬性 app:imageUrl 、app:placeHolder 直接綁定數(shù)據(jù) 呼胚。

三茄唐、結(jié)合 LiveData

DataBinding 還有個(gè)妙處在于: 可以結(jié)合 LiveData 使用

原本我們使用DataBinding蝇更,在xml中定義的variable數(shù)據(jù) 沪编,必須要繼承BaseObservable 或者使用 ObservableField,還要添加 注解 @Bindable年扩、調(diào)用notifyPropertyChanged(BR.name)蚁廓。這是為了 user.setName(name) 字段發(fā)生變化時(shí) 通知 對應(yīng)綁定View 也進(jìn)行刷新。

繼承BaseObservable

而 我們 上一篇 中 MVVM 是使用 LiveData厨幻,實(shí)現(xiàn)數(shù)據(jù)驅(qū)動(dòng)的相嵌,它包裹的 User 是沒有繼承BaseObservable的, 要繼承嘛况脆? 不用饭宾!

LiveData 的出現(xiàn),就可以代替 ObservableField 格了,并且 還自動(dòng)具備 生命周期管理看铆。

不用侵入式的修改數(shù)據(jù)實(shí)體類了,直接使用LiveData笆搓,同樣支持DataBinding的數(shù)據(jù)綁定性湿!

DataBinding 結(jié)合 LiveData 使用步驟很簡單:

  1. 要使用LiveData對象作為數(shù)據(jù)綁定來源,需要設(shè)置LifecycleOwner
  2. xml中 定義變量 ViewModel满败, 并使用 ViewModel 中的 LiveData 綁定對應(yīng)控件
  3. binding設(shè)置變量ViewModel
        //結(jié)合DataBinding使用的ViewModel
        //1. 要使用LiveData對象作為數(shù)據(jù)綁定來源肤频,需要設(shè)置LifecycleOwner
        binding.setLifecycleOwner(this);

        ViewModelProvider viewModelProvider = new ViewModelProvider(this);
        mUserViewModel = viewModelProvider.get(UserViewModel.class);
        //3. 設(shè)置變量ViewModel
        binding.setVm(mUserViewModel);
        <!-- 2. 定義ViewModel 并綁定-->
    <variable
            name="vm"
            type="com.hfy.demo01.module.jetpack.databinding.UserViewModel" />

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

這樣就ok了,你會(huì)發(fā)現(xiàn) 我們不需要在 Activity中 拿到LivaData 去 observe(owner算墨,observer)了宵荒,DataBinding 自動(dòng)生成的代碼,會(huì)幫我們?nèi)プ鲞@操作净嘀,所以需要設(shè)置LifecycleOwner报咳。

也就是說,在上一篇中介紹的 Jetpack MVVM 中挖藏,如果要使用 DataBinding 的話暑刃,也是很簡單的。

四膜眠、Jetpack MVVM 補(bǔ)充說明

講完DataBinding岩臣,所有的 Jetpack 架構(gòu)組件 的重點(diǎn)內(nèi)容 就全部講完了溜嗜。

這里對 Jetpack AAC 及 MVVM ,做一些 補(bǔ)充 和 說明:

  • 一架谎、ViewModel 和 View 職責(zé)分離炸宵,ViewModel中處理業(yè)務(wù)邏輯,View 僅展示數(shù)據(jù)及傳遞事件
  • 二谷扣、ViewModel 不引用 View 及 Context
  • 三土全、View 通過 LiveData 觀察數(shù)據(jù)變化,不是直接向View 推送數(shù)據(jù)
  • 四会涎、ViewModel中 除了 業(yè)務(wù) LiveData 外裹匙,還應(yīng)該提供 LiveData<DataState>,表示數(shù)據(jù) 是加載中在塔、加載成功幻件、加載失敗。
  • 五蛔溃、使用SingleLiveEvent 來傳遞 事件類消息:僅在顯式調(diào)用setValue()或call()時(shí) 才會(huì)通知觀察者绰沥;只有一個(gè)觀察者會(huì)收到更改通知。
  • 六贺待、ViewModel 和 Repository 之間徽曲,建議 使用 LiveData 進(jìn)行通信,就像 View 和 ViewModel 之間那樣 使用回調(diào)的話麸塞,可能會(huì)有內(nèi)存泄漏的風(fēng)險(xiǎn)秃臣。 并且在ViewModel中 使用 Transformations.switchMap 把 生命周期信息 傳遞到 Repository 的 LiveData 中。
  • 七哪工、DataBinding中綁定的數(shù)據(jù) 直接使用 LivaData 即可奥此, 而不是 BaseObservable
  • 八、xml中盡量只定義一個(gè)variable雁比,那就是 頁面對應(yīng)的 ViewModel 稚虎,控件直接綁定 LivaData 的字段
  • 九、XML 中盡量 不使用邏輯表達(dá)式偎捎,把邏輯放在 ViewModel 中蠢终,控件綁定終態(tài)數(shù)據(jù)

五、總結(jié)

本篇 重點(diǎn)講了 DataBinding 的重新認(rèn)知:DataBinding的本質(zhì) " 終態(tài)數(shù)據(jù) 綁定到 View " 茴她,而不是 ” 在xml寫邏輯 ”寻拂;自定義屬性 BindingAdapter;結(jié)合 LiveData的使用丈牢〖蓝ぃ可見DataBinding 在 Jetpack MVVM 架構(gòu)中 還是 有很大優(yōu)勢的。 最后補(bǔ)充說明得了 Jetpack MVVM 架構(gòu) 的使用注意事項(xiàng)和原則己沛,在實(shí)際項(xiàng)目使用中 應(yīng)該會(huì)很有體會(huì)朴皆。

到這里呢帕识,整個(gè)Jetpack AAC系列 也就結(jié)束了泛粹,到這里是第五篇了遂铡。每篇文章都想著盡可能把內(nèi)容 給介紹清楚,包括很多自己使用過后的理解晶姊。過程中也閱讀了大量 相關(guān)優(yōu)秀的文章 扒接,學(xué)習(xí)到了不同的觀點(diǎn)。雖然整個(gè)系列是經(jīng)過 閱讀源碼们衙、實(shí)際使用钾怔、閱讀其他優(yōu)秀文章 之后輸出的,但不免出現(xiàn)錯(cuò)誤和遺漏蒙挑,歡迎大家 留言討論宗侦。

如果覺得文章還不錯(cuò),想第一時(shí)間收到文章推送忆蚀,歡迎關(guān)注我的 公.眾.號(hào) 胡飛洋 矾利。如果有問題或者想進(jìn)群,號(hào)內(nèi)有加我微信的入口馋袜,我可以拉你入群男旗。在技術(shù)學(xué)習(xí)的道路上,我們一起前進(jìn)欣鳖!

Demo地址

參考與感謝:

DataBinding官方文檔

ViewModel 和 LiveData:為設(shè)計(jì)模式打 Call 還是唱反調(diào)察皇?

重學(xué)安卓:從 被誤解 到 真香 的 Jetpack DataBinding!

MVVM陷阱之DataBinding

.

你的 點(diǎn)贊泽台、評論什荣,是對我的巨大鼓勵(lì)!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末怀酷,一起剝皮案震驚了整個(gè)濱河市稻爬,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌胰坟,老刑警劉巖因篇,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異笔横,居然都是意外死亡竞滓,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進(jìn)店門吹缔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來商佑,“玉大人,你說我怎么就攤上這事厢塘〔杳唬” “怎么了肌幽?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長抓半。 經(jīng)常有香客問我喂急,道長,這世上最難降的妖魔是什么笛求? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任廊移,我火速辦了婚禮,結(jié)果婚禮上探入,老公的妹妹穿的比我還像新娘狡孔。我一直安慰自己,他們只是感情好蜂嗽,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布苗膝。 她就那樣靜靜地躺著,像睡著了一般植旧。 火紅的嫁衣襯著肌膚如雪辱揭。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天隆嗅,我揣著相機(jī)與錄音界阁,去河邊找鬼。 笑死胖喳,一個(gè)胖子當(dāng)著我的面吹牛泡躯,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播丽焊,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼较剃,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了技健?” 一聲冷哼從身側(cè)響起写穴,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎雌贱,沒想到半個(gè)月后啊送,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡欣孤,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年馋没,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片降传。...
    茶點(diǎn)故事閱讀 39,696評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡篷朵,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情声旺,我是刑警寧澤笔链,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站腮猖,受9級特大地震影響鉴扫,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜缚够,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一幔妨、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧谍椅,春花似錦、人聲如沸古话。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽陪踩。三九已至杖们,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間肩狂,已是汗流浹背摘完。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留傻谁,地道東北人孝治。 一個(gè)月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像审磁,于是被迫代替她去往敵國和親谈飒。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評論 2 353

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