Android中LayoutInflater詳解

前言

身為Android開(kāi)發(fā)者既琴,我們知道在用RecycleView在onCreateViewHolder方法里通常是加載Item的布局文件动羽,如下:

    @NonNull
    @Override
    public CustomerAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.adapter_row_item,parent,false);
        return new ViewHolder(v);
    }

發(fā)現(xiàn)

LayoutInflater.from(parent.getContext()).inflate(R.layout.adapter_row_item,parent,false);

RecycleView是通過(guò)LayoutInflater.from的方式將Item的根布局視圖渲染出來(lái)。

LayoutInflater是什么埂蕊?

這個(gè)LayoutInflater到底是什么呢竹伸?查看官方api奇唤,官方說(shuō)明如下:


LayoutInflater官方說(shuō)明

大概意識(shí)是:將布局xml文件實(shí)例化到對(duì)應(yīng)的視圖對(duì)象中,前提是這個(gè)視圖對(duì)象中沒(méi)有添加過(guò)這個(gè)xml布局峭拘。使用Activity.getLayoutInflate()或者Context.getSystemService(Class)來(lái)檢索這個(gè)已經(jīng)正在運(yùn)行設(shè)備上的實(shí)例俊庇。
要為自己的視圖創(chuàng)建帶有LayoutInflate.Factory的新LayoutInflate,你可以使用cloneInContext(Context)來(lái)復(fù)制現(xiàn)有的ViewFactory,然后調(diào)用其上的setFactory(LayoutInflate.Factory)棚唆。
由于性能原因暇赤,視圖膨脹嚴(yán)重依賴(lài)于構(gòu)建時(shí)對(duì)XML文件的預(yù)處理。因此宵凌,目前不可能運(yùn)行時(shí)將LayoutInflate與XmlPullParser一起在普通XML文件上使用鞋囊;它只能從編譯資源返回的XmlPullParser中使用。
這個(gè)類(lèi)的實(shí)例必須使用LayoutInflate.class的Context.getSystemService(Class)或者Context.LAYOUT_INFLATER_SERVICE的Context.getSystemService(String)獲得瞎惫。
?
其實(shí)這個(gè)類(lèi)LayoutInflater是Android開(kāi)發(fā)中經(jīng)常遇到的溜腐,因?yàn)槲覀兯鶎?xiě)的XML文件布局中都是通過(guò)LayoutInflater.from inflate成具體的View對(duì)象。但是大家會(huì)疑問(wèn)瓜喇,沒(méi)有看到呀挺益?我們平時(shí)加載布局不都是通過(guò)Activity的setContentView()來(lái)完成加載布局的嗎?通過(guò)查看setConentView可以發(fā)現(xiàn)首先調(diào)用getWindow拿到window對(duì)象乘寒,而創(chuàng)造window對(duì)象的時(shí)候就初始化了LayoutInflate對(duì)象望众,最后調(diào)用inflate方法使用pull解析方式解析XML最后返回View。
?
通過(guò)上面分析可知伞辛,LayoutInflater這個(gè)類(lèi)作用類(lèi)似findViewById()烂翰,但是LayoutInflater是用來(lái)找layout/下的xml文件進(jìn)行實(shí)例化,LayoutInflater是一個(gè)用于加載布局的系統(tǒng)服務(wù)蚤氏,就是用來(lái)實(shí)例化和布局XML文件對(duì)應(yīng)的View對(duì)象甘耿,但是它不能直接使用,需要通過(guò)getLayoutInflate()方法或者getSystemService()方法獲得和當(dāng)前Context綁定的實(shí)例竿滨。
獲取LayoutInflater實(shí)例的三個(gè)方法:

LayoutInflater inflater = LayoutInflater.from(this);  
LayoutInflater inflater = getLayoutInflater();  
LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE); 

LayoutInflater的inflate方法源碼解析

inflate的重載方法有四個(gè)佳恬,依次:

    public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
        return inflate(resource, root, root != null);
    }
    public View inflate(XmlPullParser parser, @Nullable ViewGroup root) {
        return inflate(parser, root, root != null);
    }
    public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
        final Resources res = getContext().getResources();
        if (DEBUG) {
            Log.d(TAG, "INFLATING from resource: \"" + res.getResourceName(resource) + "\" ("
                    + Integer.toHexString(resource) + ")");
        }

        final XmlResourceParser parser = res.getLayout(resource);
        try {
            return inflate(parser, root, attachToRoot);
        } finally {
            parser.close();
        }
    }
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
        synchronized (mConstructorArgs) {
            Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate");

            final Context inflaterContext = mContext;
            final AttributeSet attrs = Xml.asAttributeSet(parser);
            //mConstructorArgs 是長(zhǎng)度為2的Object 數(shù)組
            Context lastContext = (Context) mConstructorArgs[0];
            //第1個(gè)放了context對(duì)象
            mConstructorArgs[0] = inflaterContext;
            View result = root;

            try {
                // Look for the root node.
                int type;
                while ((type = parser.next()) != XmlPullParser.START_TAG &&
                        type != XmlPullParser.END_DOCUMENT) {
                    // Empty
                }

                if (type != XmlPullParser.START_TAG) {
                    throw new InflateException(parser.getPositionDescription()
                            + ": No start tag found!");
                }

                final String name = parser.getName();

                if (DEBUG) {
                    System.out.println("**************************");
                    System.out.println("Creating root view: "
                            + name);
                    System.out.println("**************************");
                }

                if (TAG_MERGE.equals(name)) {
                    if (root == null || !attachToRoot) {
                        throw new InflateException("<merge /> can be used only with a valid "
                                + "ViewGroup root and attachToRoot=true");
                    }

                    rInflate(parser, root, inflaterContext, attrs, false);
                } else {
                    // Temp is the root view that was found in the xml
                    //根據(jù)tag節(jié)點(diǎn)來(lái)創(chuàng)建View
                    final View temp = createViewFromTag(root, name, inflaterContext, attrs);

                    ViewGroup.LayoutParams params = null;

                    if (root != null) {
                        if (DEBUG) {
                            System.out.println("Creating params from root: " +
                                    root);
                        }
                        // Create layout params that match root, if supplied
                        //創(chuàng)建LayoutParams布局
                        params = root.generateLayoutParams(attrs);
                        if (!attachToRoot) {
                            // Set the layout params for temp if we are not
                            // attaching. (If we are, we use addView, below)
                            //設(shè)置布局參數(shù)
                            temp.setLayoutParams(params);
                        }
                    }

                    if (DEBUG) {
                        System.out.println("-----> start inflating children");
                    }

                    // Inflate all children under temp against its context.
                    //解析temp的子View
                    rInflateChildren(parser, temp, attrs, true);

                    if (DEBUG) {
                        System.out.println("-----> done inflating children");
                    }

                    // We are supposed to attach all the views we found (int temp)
                    // to root. Do that now.
                    if (root != null && attachToRoot) {
                        //如果root不為空,并且attachToRoot為true
                        root.addView(temp, params);
                    }

                    // Decide whether to return the root that was passed in or the
                    // top view found in xml.
                    if (root == null || !attachToRoot) {
                        result = temp;
                    }
                }

            } catch (XmlPullParserException e) {
                final InflateException ie = new InflateException(e.getMessage(), e);
                ie.setStackTrace(EMPTY_STACK_TRACE);
                throw ie;
            } catch (Exception e) {
                final InflateException ie = new InflateException(parser.getPositionDescription()
                        + ": " + e.getMessage(), e);
                ie.setStackTrace(EMPTY_STACK_TRACE);
                throw ie;
            } finally {
                // Don't retain static reference on context.
                mConstructorArgs[0] = lastContext;
                mConstructorArgs[1] = null;

                Trace.traceEnd(Trace.TRACE_TAG_VIEW);
            }
            // 如果view被添加到root則返回root 后再返回xml解析出來(lái)的view
            return result;
        }
    }

可以發(fā)現(xiàn)前面三個(gè)方法都是調(diào)用了第四個(gè)方法于游,那么重點(diǎn)看第四個(gè)方法即可:
先看官方的注釋?zhuān)?/p>

/**
     * Inflate a new view hierarchy from the specified XML node. Throws
     * {@link InflateException} if there is an error.
     * <p>
     * <em><strong>Important</strong></em>&nbsp;&nbsp;&nbsp;For performance
     * reasons, view inflation relies heavily on pre-processing of XML files
     * that is done at build time. Therefore, it is not currently possible to
     * use LayoutInflater with an XmlPullParser over a plain XML file at runtime.
     *
     * @param parser XML dom node containing the description of the view
     *        hierarchy.
     * @param root Optional view to be the parent of the generated hierarchy (if
     *        <em>attachToRoot</em> is true), or else simply an object that
     *        provides a set of LayoutParams values for root of the returned
     *        hierarchy (if <em>attachToRoot</em> is false.)
     * @param attachToRoot Whether the inflated hierarchy should be attached to
     *        the root parameter? If false, root is only used to create the
     *        correct subclass of LayoutParams for the root view in the XML.
     * @return The root View of the inflated hierarchy. If root was supplied and
     *         attachToRoot is true, this is root; otherwise it is the root of
     *         the inflated XML file.
     */

第一個(gè)參數(shù)XmlPullParser parser:這個(gè)是用來(lái)解析XML布局毁葱,布局解析器。
第二個(gè)參數(shù)ViewGroup root:這個(gè)是可選的參數(shù)贰剥,可以傳入null头谜,如果attachToRoot的值為true,返回的root是生成視圖的父類(lèi)鸠澈,如果attachToRoot的值為false柱告,root是一個(gè)對(duì)象,為返回的視圖提供一系列的LayoutParams參數(shù)笑陈。
第三個(gè)參數(shù)boolean attachToRoot:這個(gè)參數(shù)決定生成的視圖是否添加到root上际度,如果這個(gè)值為false,root僅僅用來(lái)為xml布局生成正確的布局參數(shù)涵妥。
返回參數(shù)是一個(gè)View類(lèi)型:如果root參數(shù)傳入不是null乖菱,并且attachToRoot的值是true,則返回的值是root參數(shù)蓬网,否則返回值就是XML布局的根節(jié)點(diǎn)視圖窒所。
下面提供一個(gè)XML布局,我們根據(jù)源碼一行一行走:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.recycleviewdemo.MainActivity">

    <Button
        android:id="@+id/btn_update"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
    <android.support.v7.widget.RecyclerView
        android:id="@+id/recycleview"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        app:layout_constraintTop_toBottomOf="@id/btn_update"
        app:layout_constraintBottom_toBottomOf="parent"/>
</android.support.constraint.ConstraintLayout>
final AttributeSet attrs = Xml.asAttributeSet(parser);

生成的attrs是XML布局根節(jié)點(diǎn)的參數(shù)集合

View result = root;

View類(lèi)型的result外面?zhèn)鬟f進(jìn)來(lái)的root參數(shù)帆锋,而inflate方法最后返回的是result

final View temp = createViewFromTag(root, name, inflaterContext, attrs);

生成XML布局的根節(jié)點(diǎn)temp吵取,XML布局就是上面,調(diào)用inflate方法將布局轉(zhuǎn)為View時(shí)锯厢,那么temp就是ConstraintLayout皮官。

                    if (root != null) {
                        if (DEBUG) {
                            System.out.println("Creating params from root: " +
                                    root);
                        }
                        // Create layout params that match root, if supplied
                        params = root.generateLayoutParams(attrs);
                        if (!attachToRoot) {
                            // Set the layout params for temp if we are not
                            // attaching. (If we are, we use addView, below)
                            temp.setLayoutParams(params);
                        }
                    }

如果傳遞root不為null,就執(zhí)行上面代碼实辑,根據(jù)attrs生成XML根布局的布局參數(shù)params捺氢,如果attachToRoot為false,表示XML布局生成的View不添加到root上剪撬,執(zhí)行setLayoutParams(params)摄乒,即為根節(jié)點(diǎn)temp設(shè)置布局參數(shù)。

                    // We are supposed to attach all the views we found (int temp)
                    // to root. Do that now.
                    if (root != null && attachToRoot) {
                        root.addView(temp, params);
                    }

如果root不為空残黑,并且參數(shù)attachToRoot為true馍佑,XML布局生成的View就會(huì)添加到root,執(zhí)行root.addView(temp,params)萍摊。

                    // Decide whether to return the root that was passed in or the
                    // top view found in xml.
                    if (root == null || !attachToRoot) {
                        result = temp;
                    }

如果root為空挤茄,或者attachToRoot為false,意思是沒(méi)有為XML布局設(shè)置父布局或者不想掛載在root上冰木,那么result就是temp穷劈,也就是XML布局的根節(jié)點(diǎn),那么上面XML的根節(jié)點(diǎn)就是ConstraintLayout踊沸。
最后就是返回result了歇终。

簡(jiǎn)單例子

通過(guò)上面的分析,應(yīng)該有一個(gè)大概了解逼龟,下面通過(guò)幾個(gè)小例子來(lái)實(shí)踐加深理解:
MainActivity界面布局如下:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/cl_main"
    tools:context="com.example.recycleviewdemo.MainActivity">


</android.support.constraint.ConstraintLayout>

resource指定的布局如下:
adapter_row_item.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="200dp"
    android:layout_height="200dp"
    android:background="@color/colorAccent">


    <TextView
        android:id="@+id/textview"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/recycleview_text"/>
</FrameLayout>

View inflate( int resource, ViewGroup root, boolean attachToRoot)

1.root不為null,attachToRoot為true

也就是想把a(bǔ)dapter_row_item.xml布局添加到MainActivity布局中:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ConstraintLayout cl_main = findViewById(R.id.cl_main);
        LayoutInflater inflater = getLayoutInflater();
        inflater.inflate(R.layout.adapter_row_item,cl_main,true);

    }

效果如下圖所示:


添加指定布局到root上

效果確實(shí)如上面分析评凝。也就是當(dāng)attachToRoot為true,會(huì)自動(dòng)把第一個(gè)參數(shù)resource的布局文件解析成View后添加到root所指定的View中腺律。
所以不用調(diào)用addView方法刻意添加奕短。如果加多addView:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ConstraintLayout cl_main = findViewById(R.id.cl_main);
        LayoutInflater inflater = getLayoutInflater();
        View view = inflater.inflate(R.layout.adapter_row_item,cl_main,true);
        cl_main.addView(view);
    }

會(huì)拋出下面異常:

     Caused by: java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.

意思是不能重復(fù)添加宜肉。

2.root不為null,attachToRoot為false

表示不將第一個(gè)參數(shù)指定的View添加到root,這種方式和直接將root設(shè)為null是有區(qū)別的翎碑。如果root為null谬返,那么第一個(gè)參數(shù)所指定的View的根節(jié)點(diǎn)layout_width和layout_height就會(huì)失效,因?yàn)檫@個(gè)View不在任何容器上日杈,如果現(xiàn)在想讓這個(gè)View的根節(jié)點(diǎn)有效遣铝,又不想讓它在任何一個(gè)容器中,那就可以設(shè)置root不為null莉擒,attachToRoot為false酿炸。

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ConstraintLayout cl_main = findViewById(R.id.cl_main);
        LayoutInflater inflater = getLayoutInflater();
        inflater.inflate(R.layout.adapter_row_item,cl_main,false);

    }

實(shí)際效果adapter_row_item.xml解析出來(lái)的View沒(méi)有添加到MainActivity布局上,得要加

        View view = inflater.inflate(R.layout.adapter_row_item,cl_main,false);
        cl_main.addView(view);

實(shí)際和上面效果一致涨冀。

3.root為null

當(dāng)root為null的時(shí)候填硕,無(wú)論attachToRoot的值為false還是true,效果都是一樣的蝇裤,因?yàn)椴粚⒌谝粋€(gè)參數(shù)指定的View添加到任何容器中廷支,也沒(méi)用任何容器約束第一個(gè)參數(shù)所指定的View來(lái)生成布局參數(shù):

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ConstraintLayout cl_main = findViewById(R.id.cl_main);
        LayoutInflater inflater = getLayoutInflater();
        View view = inflater.inflate(R.layout.adapter_row_item,null,false);
        cl_main.addView(view);
    }

實(shí)際效果如下:


root為null

無(wú)論我設(shè)置FrameLayout的根節(jié)點(diǎn)寬高什么都是沒(méi)有效果的,如果我設(shè)置TextView的屬性就會(huì)有變化栓辜。

View inflate(int resource, ViewGroup root)

1.root為null

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ConstraintLayout cl_main = findViewById(R.id.cl_main);
        LayoutInflater inflater = getLayoutInflater();
        View view = inflater.inflate(R.layout.adapter_row_item,null);
        cl_main.addView(view);
    }

實(shí)際效果和root為null的情況一樣

2.root不為null

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ConstraintLayout cl_main = findViewById(R.id.cl_main);
        LayoutInflater inflater = getLayoutInflater();
        inflater.inflate(R.layout.adapter_row_item,cl_main);
  
    }

實(shí)際效果和root不為null,attachToRoot為true情況一致恋拍。因?yàn)槭冀K還是調(diào)用了三個(gè)參數(shù)的方法,只不過(guò)當(dāng)root為null藕甩,的時(shí)候attachToRoot的值就會(huì)設(shè)為false施敢,如果root不為null,attachToRoot的值為true狭莱。

總結(jié)

通過(guò)初步源碼的分析和簡(jiǎn)單例子實(shí)踐僵娃,對(duì)LayoutInflater和inflate方法有一定了解,其實(shí)root就是為XML布局生成的View指定一個(gè)父類(lèi)腋妙,讓其添加在上面默怨,root如果不為null,那么XML布局的根節(jié)點(diǎn)就會(huì)生成布局參數(shù)骤素,由attachToRoot決定這個(gè)View想不想成為root的孩子匙睹。root如果為null,由于XML布局所指定的View沒(méi)有處于任何一個(gè)容器中济竹,所以根節(jié)點(diǎn)的寬高屬性失效痕檬,但是可以通過(guò)addView加載到root上。所以現(xiàn)在知道文章剛開(kāi)始RecycleView在onCreateViewHolder方法里通常是加載Item的布局文件方法為啥要將attachToRoot設(shè)置為false送浊,因?yàn)槲覀儾回?fù)責(zé)將layout文件的View添加進(jìn)ViewGroup梦谜,ListView和Recycleview負(fù)責(zé)決定什么時(shí)候展示它的子View,這個(gè)我們決定不了。
注意:

  1. 當(dāng)我們不負(fù)責(zé)將layout文件的View添加進(jìn)ViewGroup的情況下設(shè)置attachToRoot參數(shù)為false唁桩。
  2. 不要在View已經(jīng)被添加進(jìn)ViewGroup時(shí)傳入true闭树。
  3. 自定義View時(shí)很適合將attachToRoot設(shè)置為true。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末朵夏,一起剝皮案震驚了整個(gè)濱河市蔼啦,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌仰猖,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,839評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件奈籽,死亡現(xiàn)場(chǎng)離奇詭異饥侵,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)衣屏,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)躏升,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人狼忱,你說(shuō)我怎么就攤上這事膨疏。” “怎么了钻弄?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,116評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵佃却,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我窘俺,道長(zhǎng)饲帅,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,371評(píng)論 1 279
  • 正文 為了忘掉前任瘤泪,我火速辦了婚禮灶泵,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘对途。我一直安慰自己赦邻,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布实檀。 她就那樣靜靜地躺著惶洲,像睡著了一般。 火紅的嫁衣襯著肌膚如雪劲妙。 梳的紋絲不亂的頭發(fā)上湃鹊,一...
    開(kāi)封第一講書(shū)人閱讀 49,111評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音镣奋,去河邊找鬼币呵。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的余赢。 我是一名探鬼主播芯义,決...
    沈念sama閱讀 38,416評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼妻柒!你這毒婦竟也來(lái)了扛拨?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,053評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤举塔,失蹤者是張志新(化名)和其女友劉穎绑警,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體央渣,經(jīng)...
    沈念sama閱讀 43,558評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡计盒,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了芽丹。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片北启。...
    茶點(diǎn)故事閱讀 38,117評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖拔第,靈堂內(nèi)的尸體忽然破棺而出咕村,到底是詐尸還是另有隱情,我是刑警寧澤蚊俺,帶...
    沈念sama閱讀 33,756評(píng)論 4 324
  • 正文 年R本政府宣布懈涛,位于F島的核電站,受9級(jí)特大地震影響春叫,放射性物質(zhì)發(fā)生泄漏肩钠。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評(píng)論 3 307
  • 文/蒙蒙 一暂殖、第九天 我趴在偏房一處隱蔽的房頂上張望价匠。 院中可真熱鬧,春花似錦呛每、人聲如沸踩窖。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,315評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)洋腮。三九已至,卻和暖如春手形,著一層夾襖步出監(jiān)牢的瞬間啥供,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,539評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工库糠, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留伙狐,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,578評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像贷屎,于是被迫代替她去往敵國(guó)和親罢防。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評(píng)論 2 345

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