【Android】DataBinding(MVVM設(shè)計模式)

*本篇文章已授權(quán)微信公眾號 guolin_blog (郭霖)獨家發(fā)布

什么是MVVM

說到DataBinding而涉,就有必要先提起MVVM設(shè)計模式晦嵌。
Model–View–ViewModel(MVVM) 是一個軟件架構(gòu)設(shè)計模式来吩,相比MVVM,大家對MVC或MVP可能會更加熟悉。

  • MVC:(VIew-Model-Controller)
    早期將VIew秦忿、Model、Controller代碼塊進行劃分蛾娶,使得程序大部分分離灯谣,降低耦合。
  • MVP:(VIew-Model-Presenter)由于MVC中View和Model之間的依賴太強蛔琅,導(dǎo)致Activity中的代碼過于臃腫胎许。為了他們可以絕對獨立的存在,慢慢演化出了MVP罗售。在MVP中View并不直接使用Model辜窑,它們之間的通信是通過 Presenter (MVC中的Controller)來進行的。
  • MVVM:(Model–View–ViewModel)
    MVVM可以算是MVP的升級版寨躁,將 Presenter 改名為 ViewModel穆碎。關(guān)鍵在于View和Model的雙向綁定,當(dāng)View有用戶輸入后职恳,ViewModel通知Model更新數(shù)據(jù)所禀,同理Model數(shù)據(jù)更新后方面,ViewModel通知View更新。

Data Binding

在Google I/O 2015上北秽,伴隨著Android M預(yù)覽版發(fā)布的Data Binding兼容函數(shù)庫葡幸。
不知道要扯什么了,還是直接上代碼贺氓,來看看Data Binding的魅力吧蔚叨。

  • 環(huán)境要求

Data Binding對使用的環(huán)境還是有一定要求的(這貨有點挑)
Android Studio版本在1.3以上
gradle的版本要在1.5.0-alpha1以上
需要在Android SDK manager中下載Android Support repository
然后在對應(yīng)的Module的build.gradle中添加

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

Gradle需要升級版本的可以參考升級Gradle版本

  • 創(chuàng)建對象

創(chuàng)建一個User類

public class User {
    private String firstName;
    private String lastName;

    public User(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    public String getFirstName() {
        return this.firstName;
    }

    public String getLastName() {
        return this.lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }
}
  • 布局

在activity_main.xml中布局

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <import type="com.example.gavin.databindingtest.User"/>
        <variable
            name="user"
            type="User" />
    </data>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:gravity="center"
        >
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{user.firstName}"
            android:textSize="20sp" />
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{user.lastName}"
            android:textSize="25sp" />
    </LinearLayout>
</layout>

這里跟平時的布局有點不同,最外層是layout辙培,里面分別是是data以及我們的布局蔑水。
data:聲明了需要用到的user對象,type用于是定路徑扬蕊。
可以在TextView中的看到android:text="@{user.firstName}"搀别, 這是什么鬼,沒見過這么寫的N惨帧P浮!
(不急再愈,繼續(xù)往下看)

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

看看下面的MainActivity

public class MainActivity extends AppCompatActivity {
    private ActivityMainBinding binding;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        User user = new User("Micheal", "Jack");
        binding.setUser(user);
    }
}

問我ActivityMainBinding哪來的榜苫?我怎么知道...
ActivityMainBinding是根據(jù)布局文件的名字生成的,在后面加了Binding翎冲。
運行下看看效果吧

效果

有點懵逼了垂睬,就綁定了下而已,這些數(shù)據(jù)是怎么顯示到界面上的抗悍。


懵逼

他是怎么工作的驹饺?
原來Data Binding 在程序代碼正在編譯的時候,找到所有它需要的信息缴渊。然后通過語法來解析這些表達式赏壹,最后生成一個類。
通過反編譯我們可以看到衔沼,Data Binding為我們生成了databinding包卡儒,以及ActivityMainBinding類(反編譯可以參考這里


看看我們在onCreate中最后調(diào)用的binding.setUser(user),在ActivityMainBinding中可以看到這個方法俐巴。
setUser方法

我想就是這個 super.requestRebind()對數(shù)據(jù)進行了綁定骨望,至于里面怎么實現(xiàn)的,有待進一步研究欣舵。

更多用法

上面只是用一個簡單的例子擎鸠,展示了Data Binding的用法,如果想在實際項目中使用缘圈,可不是上面這例子可以搞定的劣光。下面就來說說Data Bindig的更多用法袜蚕。

  • 首先消除下大家對空指針的顧慮

自動生成的 DataBinding 代碼會檢查null,避免出現(xiàn)NullPointerException绢涡。
例如在表達式中@{user.phone}如果user == null 那么會為user.phone設(shè)置默認值null而不會導(dǎo)致程序崩潰(基本類型將賦予默認值如int為0牲剃,引用類型都會賦值null)

  • 自定義DataBinding名

如果不喜歡自動生成的Data Binding名,我們可以自己來定義

<data class="MainBinding">
    ....
</data>

class對應(yīng)的就是生成的Data Binding名

  • 導(dǎo)包

跟Java中的用法相似雄可,布局文件中支持import的使用凿傅,原來的代碼是這樣

<data>
     <variable name="user" type="com.example.gavin.databindingtest.User" />
 </data>

使用import后可以寫成這樣:

    <data>
        <import type="com.example.gavin.databindingtest.User"/>
        <variable
            name="user"
            type="User" />
    </data>

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
遇到相同的類名的時候:

<data>
    <import type="com.example.gavin.databindingtest.User" alias="User"/>
    <import type="com.example.gavin.mc.User" alias="mcUser"/>
    <variable name="user" type="User"/>
    <variable name="mcUser" type="mcUser"/>
</data>

使用alias設(shè)置別名,這樣user對應(yīng)的就是com.example.gavin.databindingtest.User数苫,mcUser就對應(yīng)com.example.gavin.mc.User聪舒,然后

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

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
當(dāng)需要用到一些包時,在Java中可以自動導(dǎo)包虐急,不過在布局文件中就沒有這么方便了箱残。需要使用import導(dǎo)入這些包,才能使用止吁。如被辑,需要用到View的時候

<data>
    <import type="android.view.View"/>
</data>
...
<TextView
...
android:visibility="@{user.isStudent ? View.VISIBLE : View.GONE}"
/>

注意只要是在Java中需要導(dǎo)入包的類,這邊都需要導(dǎo)入敬惦,如:Map敷待、ArrayList等,不過java.lang包里的類是可以不用導(dǎo)包的

  • 表達式

在布局中仁热,不僅可以使用

android:text="@{user.lastName}"

還可以使用表達式如:

三元運算

在User中添加boolean類型的isStudent屬性,用來判斷是否為學(xué)生勾哩。

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text='@{user.isStudent? "Student": "Other"}'
android:textSize="30sp"/>

注意需要用到雙引號的時候抗蠢,外層的雙引號改成單引號。
還可以這樣用

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="學(xué)生"
android:visibility="@{user.isStudent ? View.VISIBLE : View.GONE}"
android:textSize="30sp"/>

這里用到的View需要在data中聲明

<data>
     <import type="android.view.View"/>
</data>

注意:android:visibility="@{user.isStudent ? View.VISIBLE : View.GONE}",可能會被標(biāo)記成紅色思劳,不用管它編譯會通過的

迅矛??

除了常用的操作法潜叛,另外還提供了一個 null 的合并運算符號 ??秽褒,這是一個三目運算符的簡便寫法。

contact.lastName ?? contact.name

相當(dāng)于

contact.lastName != null ? contact.lastName : contact.name

所支持的操作符如下:
數(shù)學(xué)運算符 + - / * %
字符串拼接 +
邏輯運算 && ||
二進制運算 & | ^
一元運算符 + - ! ~
位運算符 >> >>> <<
比較運算符 == > < >= <=
instanceof
Grouping ()
文字 - character, String, numeric, null
類型轉(zhuǎn)換 cast
方法調(diào)用 methods call
字段使用 field access
數(shù)組使用 [] Arrary access
三元運算符 ? :

  • 顯示圖片

除了文字的設(shè)置威兜,網(wǎng)絡(luò)圖片的顯示也是我們常用的销斟。來看看Data Binding是怎么實現(xiàn)圖片的加載的逻澳。
首先要提到BindingAdapter注解廊宪,這里創(chuàng)建了一個類刁绒,里面有顯示圖片的方法几晤。

public class ImageUtil {
    /**
     * 使用ImageLoader顯示圖片
     * @param imageView
     * @param url
     */
    @BindingAdapter({"bind:image"})
    public static void imageLoader(ImageView imageView, String url) {
        ImageLoader.getInstance().displayImage(url, imageView);
    }
}

(這方法必須是public static的贷帮,否則會報錯)
這里只用了bind聲明了一個image自定義屬性,等下在布局中會用到隶垮。
這個類中只有一個靜態(tài)方法imageLoader稚字,里面有兩參數(shù),一個是需要設(shè)置圖片的view涝动,另一個是對應(yīng)的Url迈勋,這里使用了ImageLoader庫加載圖片。
看看吧它的布局是什么樣的吧

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

    <data >
        <variable
            name="imageUrl"
            type="String"/>
    </data>

    <LinearLayout

        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:gravity="center"
        >
        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:image = "@{imageUrl}"/>
    </LinearLayout>
</layout>

最后在MainActivity中綁定下數(shù)據(jù)就可以了

binding.setImageUrl(
    "http://115.159.198.162:3000/posts/57355a92d9ca741017a28375/1467250338739.jpg");

哇靠4姿凇C夜健!就這樣昔穴?我都沒看出來它是怎么設(shè)置這些圖片的镰官。
不管了,先看看效果吗货。(其中的原理以后慢慢嘮泳唠,這里就負責(zé)說明怎么使用,這篇已經(jīng)夠長了宙搬,不想再寫了)

看個美女壓壓驚

使用BindingAdapter的時候笨腥,我這還出現(xiàn)了這樣的提示,不過不影響運行勇垛。不知道你們會不會...


【已解決】
感謝顏路同學(xué)指出@BindingAdapter({"bind:image"}) 改成@BindingAdapter({"image"}) 就不會有警告了

  • 點擊事件

在MainActivity中聲明方法:

//參數(shù)View必須有脖母,必須是public,參數(shù)View不能改成對應(yīng)的控件闲孤,只能是View谆级,否則編譯不通過
public void onClick(View view) {
    Toast.makeText(this,"點擊事件", Toast.LENGTH_LONG).show();
}

布局中:

    <data>
      ...
        <variable
        name="mainActivity"
        type="com.example.gavin.databindingtest.MainActivity"/>
    </data>
    ....
        <Button
            ...
            android:onClick="@{mainActivity.onClick}"
            />

最后記得在MainActivity中調(diào)用

binding.setMainActivity(this);

(發(fā)現(xiàn):布局文件中,variable中的name讼积,在binding中都會生成一個對應(yīng)的set方法肥照,如:setMainActivity。有set方法勤众,那就應(yīng)該有g(shù)et方法舆绎,試試getMainActivity,還真有)
運行下看看效果

點擊事件

當(dāng)然如果你不想吧點擊事件寫在MainActivity中们颜,你把它單獨寫在一個類里面:

public class MyHandler {
    public void onClick(View view) {
        Toast.makeText(view.getContext(), "點擊事件", Toast.LENGTH_LONG).show();
    }
}
    <data>
      ...
        <variable
        name="handle"
        type="com.example.gavin.databindingtest.MyHandler"/>
    </data>
    ....
        <Button
            ...
            android:onClick="@{handle.onClick}"
            />
    </data>

在MainActivity調(diào)用

binding.setHandle(new MyHandler());
  • 調(diào)用Activity中的變量

上面看到它調(diào)用MainActivity中的onClick方法吕朵,那么可以調(diào)用MainActivity中的屬性嗎?
在MainActivity中定義mName窥突,

public static String mName = "MM";

布局中

    <data>
        ...
        <variable
            name="mainActivity"
            type="com.example.gavin.databindingtest.MainActivity"/>
    </data>
        <Button
            ...
            android:text="@{mainActivity.mName}"
            />

注意這個變量必須是public static

  • 數(shù)據(jù)改變時更新UI

當(dāng)數(shù)據(jù)發(fā)生變化時努溃,我們可以這樣更新UI

    private ActivityMainBinding binding;
    private User user;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        user = new User("Micheal", "Jack");
        binding.setUser(user);
        binding.setHandle(new MyHandler());
        delay();
    }
    /**
     * 兩秒后改變firstName
     */
    private void delay() {
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                user.setFirstName("Com");
                binding.setUser(user);
            }
        }, 2000);
    }

看看調(diào)用的這個setUser是什么:


setUser

從反編譯的代碼中可以看出,setUser方法中重新綁定了數(shù)據(jù)阻问。
看下效果

效果
  • BaseObservable

使用上面的代碼實現(xiàn)了UI的更新你就滿足了茅坛?其實官方為我們提供了更加簡便的方式,使User繼承BaseObservable,代碼如下

public class User extends BaseObservable {
    private String firstName;
    private String lastName;

    public User(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }
    @Bindable
    public String getFirstName() {
        return this.firstName;
    }
    @Bindable
    public String getLastName() {
        return this.lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
        notifyPropertyChanged(BR.lastName);
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
        notifyPropertyChanged(BR.firstName);
    }
}

只要user發(fā)生變化贡蓖,就能達到改變UI的效果曹鸠。在MainActivity中只要調(diào)用以下代碼

user.setFirstName("Com");

有了BaseObservable就夠了?不不不斥铺,我比較懶彻桃,不想寫那么多@Bindable和notifyPropertyChanged。萬一里面有幾十個屬性晾蜘,那不寫哭起來邻眷?而且還有可能寫丟了。
Data Binding的開發(fā)者貼心得為我們準(zhǔn)備了一系列的ObservableField剔交,包括: ObservableBoolean, ObservableByte, ObservableChar, ObservableShort, ObservableInt, ObservableLong, ObservableFloat,ObservableDouble, 以及 ObservableParcelable看看它們的用法
ObservableField的使用
1肆饶、創(chuàng)建User2

public class User2 {
    public final ObservableField<String> firstName = new ObservableField<>();
    public final ObservableField<String> lastName = new ObservableField<>();
    public final ObservableInt age = new ObservableInt();
    public final ObservableBoolean isStudent = new ObservableBoolean();
}

這類里面沒有Get/Set。
2岖常、布局文件

        <TextView
            ...
            android:text="@{user2.firstName}" />
        <TextView
            ...
            android:text="@{user2.lastName}" />
        <TextView
            ...
            android:text="@{String.valueOf(user2.age)}"
             />

3驯镊、MainActivity中

        mUser2 = new User2();
        binding.setUser2(mUser2);
        mUser2.firstName.set("Mr");
        mUser2.lastName.set("Bean");
        mUser2.age.set(20);
        mUser2.isStudent.set(false);

這里new了一個User2對象后,直接就綁定了竭鞍。之后只要mUser2中的數(shù)據(jù)發(fā)生變化板惑,UI也會隨之更新。
除了這幾個Map跟List也是必不可少的偎快,Data Binding為我們提供了 ObservableArrayMapObservableArrayList冯乘。
ObservableArrayMap的使用

ObservableArrayMap<String, Object> user = new ObservableArrayMap<>();
user.put("firstName", "Google");
user.put("lastName", "Inc.");
user.put("age", 17);
<data>
    <import type="android.databinding.ObservableMap"/>
    <variable name="user" type="ObservableMap<String, Object>"/>
</data>
…
<TextView
   android:text='@{user["lastName"]}'
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"/>
<TextView
   android:text='@{String.valueOf(1 + (Integer)user["age"])}'
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"/>

ObservableArrayList的使用

ObservableArrayList<Object> user = new ObservableArrayList<>();
user.add("Google");
user.add("Inc.");
user.add(17);
<data>
    <import type="android.databinding.ObservableList"/>
    <import type="com.example.my.app.Fields"/>
    <variable name="user" type="ObservableList<Object>"/>
</data>
…
<TextView
   android:text='@{user[Fields.LAST_NAME]}'
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"/>
<TextView
   android:text='@{String.valueOf(1 + (Integer)user[Fields.AGE])}'
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"/>

在布局中使用中文時,編譯無法通過晒夹。

android:text='@{user2.isStudent?"學(xué)生":"非學(xué)生"}'

感謝呂檀溪同學(xué)的解決方案:
這是java環(huán)境的問題裆馒,在系統(tǒng)環(huán)境變量中增加一個變量,變量名為: JAVA_TOOL_OPTIONS丐怯, 變量值為:-Dfile.encoding=UTF-8喷好,保存。要重啟一次電腦响逢,中文就解決了,但是在某些地方棕孙,編譯的時候控制臺會出現(xiàn)部分亂

  • 在RecyclerView或ListView中使用

前面說了那么多基礎(chǔ)的用法舔亭,可還是不能達到我們的需求。幾乎在每個app中都有列表的存在蟀俊,RecyclerView或ListView钦铺,從上面所說的似乎還看不出Data Binding在RecyclerView或ListView中是否也能起作用。(用屁股想也知道肢预,Google的開發(fā)團對怎么可能會犯這么低級的錯誤)矛洞。下面以RecyclerView為例子:
1、直接看Item的布局(user_item.xml):

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable
            name="user2"
            type="com.example.gavin.databindingtest.User2" />
    </data>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="10dp"
        android:orientation="horizontal">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{user2.firstName}"/>
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="·"/>
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{user2.lastName}"/>
        <View
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:layout_weight="1"/>
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text='@{user2.age+""}'/>
    </LinearLayout>
</layout>

2、RecyclerView的數(shù)據(jù)綁定是在Adapter中完成的沼本,下面看看Adapter(這里使用了一個Adapter噩峦,如果你在使用的時候發(fā)現(xiàn)RecyclerView的動畫沒了,去這里尋找答案)

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyHolder> {

    private List<User2> mData = new ArrayList<>();

    public MyAdapter(List<User2> data) {
        this.mData = data;
    }

    @Override
    public MyHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return MyHolder.create(LayoutInflater.from(parent.getContext()), parent);
    }

    @Override
    public void onBindViewHolder(MyHolder holder, int position) {
        holder.bindTo(mData.get(position));
    }

    @Override
    public int getItemCount() {
        if (mData == null)
            return 0;
        return mData.size();
    }

    static class MyHolder extends RecyclerView.ViewHolder {
        private UserItemBinding mBinding;

        static MyHolder create(LayoutInflater inflater, ViewGroup parent) {
            UserItemBinding binding = UserItemBinding.inflate(inflater, parent, false);
            return new MyHolder(binding);
        }

        private MyHolder(UserItemBinding binding) {
            super(binding.getRoot());
            this.mBinding = binding;
        }

        public void bindTo(User2 user) {
            mBinding.setUser2(user);
            mBinding.executePendingBindings();
        }

    }
}

3抽兆、最后在布局和MainActivity中的使用跟平時的用法一樣
布局中加入RecyclerView:

        <android.support.v7.widget.RecyclerView
            android:id="@+id/recycler_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>

MainActivity中:

        List<User2> data = new ArrayList<>();
        for (int i = 0; i < 20; i++) {
            User2 user2 = new User2();
            user2.age.set(30);
            user2.firstName.set("Micheal " + i);
            user2.lastName.set("Jack " + i);
            data.add(user2);
        }
        RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
        LinearLayoutManager layoutManager = new LinearLayoutManager(
                this, LinearLayoutManager.VERTICAL, false);
        recyclerView.setLayoutManager(layoutManager);
        recyclerView.setAdapter(new MyAdapter(data));

這樣就可以了识补。
不過,在自動生成的ActivityMainBinding中辫红,我們可以看到根據(jù)RecyclerView的id凭涂,會自動生成一個recyclerView。



所以在MainActivity中贴妻,我們可以不用findViewById切油,直接使用binding.recyclerView。

        LinearLayoutManager layoutManager = new LinearLayoutManager(
                this, LinearLayoutManager.VERTICAL, false);
        binding.recyclerView.setLayoutManager(layoutManager);
        binding.recyclerView.setAdapter(new MyAdapter(data));

來看看效果吧:


RecyclerView

Tips:

  • 1:若需要顯示int類型名惩,需要加上"":如

user.age為int類型澎胡,需要這樣用

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

<TextView   
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:text="@{String.valueOf(user.age)}"/>
  • 2:不建議新手使用,出現(xiàn)錯誤的時候根據(jù)提示绢片,不容易找到出錯位置滤馍。(是根本找不到...)

參考

Google官方(權(quán)威,不過全英文底循。點擊事件寫的好像不對巢株,后來去其他地方查的):
Realm(十分全面):
CSDN-亓斌(有點像google文檔的翻譯版,整體結(jié)果相似):
陽春面的博客(好奇怪的名字)

源碼地址https://github.com/Gavin-ZYX/DataBindingTest

以上有錯誤之處感謝指出

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末熙涤,一起剝皮案震驚了整個濱河市阁苞,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌祠挫,老刑警劉巖那槽,帶你破解...
    沈念sama閱讀 219,427評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異等舔,居然都是意外死亡骚灸,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評論 3 395
  • 文/潘曉璐 我一進店門慌植,熙熙樓的掌柜王于貴愁眉苦臉地迎上來甚牲,“玉大人,你說我怎么就攤上這事蝶柿≌筛疲” “怎么了?”我有些...
    開封第一講書人閱讀 165,747評論 0 356
  • 文/不壞的土叔 我叫張陵交汤,是天一觀的道長雏赦。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么星岗? 我笑而不...
    開封第一講書人閱讀 58,939評論 1 295
  • 正文 為了忘掉前任填大,我火速辦了婚禮,結(jié)果婚禮上伍茄,老公的妹妹穿的比我還像新娘栋盹。我一直安慰自己,他們只是感情好敷矫,可當(dāng)我...
    茶點故事閱讀 67,955評論 6 392
  • 文/花漫 我一把揭開白布例获。 她就那樣靜靜地躺著,像睡著了一般曹仗。 火紅的嫁衣襯著肌膚如雪榨汤。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,737評論 1 305
  • 那天怎茫,我揣著相機與錄音收壕,去河邊找鬼。 笑死轨蛤,一個胖子當(dāng)著我的面吹牛蜜宪,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播祥山,決...
    沈念sama閱讀 40,448評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼圃验,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了缝呕?” 一聲冷哼從身側(cè)響起澳窑,我...
    開封第一講書人閱讀 39,352評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎供常,沒想到半個月后摊聋,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,834評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡栈暇,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,992評論 3 338
  • 正文 我和宋清朗相戀三年麻裁,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片源祈。...
    茶點故事閱讀 40,133評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡煎源,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出新博,到底是詐尸還是另有隱情薪夕,我是刑警寧澤脚草,帶...
    沈念sama閱讀 35,815評論 5 346
  • 正文 年R本政府宣布赫悄,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏埂淮。R本人自食惡果不足惜姑隅,卻給世界環(huán)境...
    茶點故事閱讀 41,477評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望倔撞。 院中可真熱鬧讲仰,春花似錦、人聲如沸痪蝇。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽躏啰。三九已至趁矾,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間给僵,已是汗流浹背毫捣。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留帝际,地道東北人蔓同。 一個月前我還...
    沈念sama閱讀 48,398評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像蹲诀,于是被迫代替她去往敵國和親斑粱。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,077評論 2 355

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