View的工作原理(一)測(cè)量過(guò)程

在Android體系中View作為視覺(jué)上的呈現(xiàn)挂据,扮演著非常重要的角色娃弓。盡管Android提供了一套包含很多控件的GUI庫(kù)雾狈。但是在大多數(shù)情況下烛恤,因?yàn)榻换セ蛘宫F(xiàn)的定制化要求母怜,我們不是不能直接拿來(lái)使用的。怎么解決這一問(wèn)題呢缚柏?

為了防止Android應(yīng)用界面的同類化嚴(yán)重苹熏,我們需要通過(guò)自定義View來(lái)實(shí)現(xiàn)更友好的用戶體驗(yàn)和更美觀的呈現(xiàn)效果。

前言

為了更好的自定義View币喧,我們應(yīng)該掌握View的基本流程轨域,如:View的測(cè)量流程、布局流程及繪制流程粱锐;

除了三大流程之外疙挺,View常見(jiàn)的回調(diào)方法也是需要熟練掌握的。

  1. 構(gòu)造方法:
    基本屬性和自定義屬性的初始化和賦值怜浅;
  2. onAttach
  3. onVisibilityChanged
  4. onDetach

View的工作原理

ViewRoot對(duì)應(yīng)于ViewRootImpl類铐然,它是連接WindowManger和DecorView的紐帶,View的三大流程均是通過(guò)ViewRoot來(lái)完成的恶座。

在ActivityThread中搀暑,當(dāng)Activity對(duì)象創(chuàng)建完畢后,會(huì)將DecorView添加到Window中跨琳,同時(shí)創(chuàng)建ViewRootImpl對(duì)象自点,并將ViewRootImpl對(duì)象和DecorView建立關(guān)聯(lián)。參看下面源碼(位于android-25/android/app/ActivityThread.java
):

                //該代碼位于handleLaunchActivity->handleResumeActivity方法中
                ActivityClientRecord r = mActivities.get(token);
                ...
                r.window = r.activity.getWindow();
                View decor = r.window.getDecorView();
                decor.setVisibility(View.INVISIBLE);
                ViewManager wm = a.getWindowManager();
                WindowManager.LayoutParams l = r.window.getAttributes();
                a.mDecor = decor;
                l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
                l.softInputMode |= forwardBit;
                if (r.mPreserveWindow) {
                    a.mWindowAdded = true;
                    r.mPreserveWindow = false;
                    // Normally the ViewRoot sets up callbacks with the Activity
                    // in addView->ViewRootImpl#setView. If we are instead reusing
                    // the decor view we have to notify the view root that the
                    // callbacks may have changed.
                    ViewRootImpl impl = decor.getViewRootImpl();
                    if (impl != null) {
                        impl.notifyChildRebuilt();
                    }
                }

View的繪制流程是從ViewRoot的performTraversals方法開(kāi)始的脉让,經(jīng)過(guò)measure桂敛、layout和draw三個(gè)過(guò)程才最終將一個(gè)View繪制出來(lái)功炮。其中:

  1. measure:用來(lái)測(cè)量View的寬和高;
  2. layout:用來(lái)View在父容器放置的位置术唬;
  3. draw:負(fù)責(zé)將View繪制在屏幕上薪伏;
performTraversals 的工作流程圖

如圖所示,performTraversals會(huì)依次調(diào)用performMeasure粗仓、performLayout和performDraw三個(gè)方法嫁怀,這三個(gè)方法分別完成頂級(jí)View的measure、layout和draw這三大流程借浊;其中performMeasure中會(huì)調(diào)用measure方法塘淑,measure方法又會(huì)調(diào)用onMeasure方法,在onMeasure方法則會(huì)對(duì)所有的子View進(jìn)行measure過(guò)程蚂斤,這時(shí)measure流程就從父容器傳遞到子元素中去存捺。然后子元素重復(fù)父容器的measure過(guò)程,如此反復(fù)就完成了整個(gè)View樹(shù)的遍歷曙蒸,這樣就完成了依次measure過(guò)程召噩。另外兩個(gè)過(guò)程是類似的,不贅述。

Measure過(guò)程

measure過(guò)程決定了View的寬高逸爵,measure完成以后可以通過(guò)getMeasureWidth和getMeasureHeight方法來(lái)獲取View測(cè)量后的寬高,幾乎所有情況下這兩個(gè)值都等同于View的最終寬高值凹嘲,特殊情況下除外(onLayout時(shí)強(qiáng)制指定寬高)师倔。

Layout過(guò)程

layout過(guò)程決定了View的四個(gè)頂點(diǎn)的坐標(biāo)和實(shí)際的View寬高。方法完成后周蹭,可以通過(guò)getTop趋艘、getBottom、getLeft凶朗、getRight來(lái)拿到View四個(gè)頂點(diǎn)的位置瓷胧,并可以通過(guò)getWidth、getHeight方法來(lái)拿到View最終寬高棚愤。

Draw過(guò)程

draw過(guò)程決定了View的顯示內(nèi)容搓萧,只有draw方法完成之后View的內(nèi)容才能顯示在屏幕上。

DecorView層級(jí)組成

DecorView作為頂級(jí)的View宛畦,一般情況下它內(nèi)部會(huì)包含一個(gè)豎直方向的LinearLayout瘸洛,這個(gè)LinearLayout里面有上下兩部分(具體情況和系統(tǒng)版本及主題有關(guān)),上面是標(biāo)題欄次和,下面是內(nèi)容區(qū)反肋。

Activity通過(guò)setContentView所設(shè)置的布局其實(shí)是添加到DecorView的內(nèi)容區(qū)里面了。而內(nèi)容區(qū)的id是content踏施,因此可以理解制定布局的方式不叫setView而叫setContentView石蔗;確切的說(shuō)罕邀,設(shè)置的布局是加在id為content的FrameLayout的布局中。

如圖:

頂級(jí)View: DecorView 的 結(jié)構(gòu)圖

理解MeasureSpec

MeasureSpec通過(guò)將高2位的SpecMode和低30位的SpecSize打包成一個(gè)32位的int里面來(lái)避免過(guò)多的內(nèi)存分配养距。為了方便操作诉探,其同時(shí)提供了打包和解包方法。

SpecMode

  • UNSPECIFIED(未指明模式)
    父容器不對(duì)View做任何限制铃在,要多大給多大阵具,這種情況下一般用于系統(tǒng)內(nèi)部,表示一種測(cè)量的狀態(tài)定铜。

  • EXACTLY(準(zhǔn)確模式)
    父容器已經(jīng)檢測(cè)出View所需要的精確大小阳液,這時(shí)View的最終大小就是低30位的SpecSize指定的值。這種情況對(duì)應(yīng)于LayoutParams中的match_parent和具體的數(shù)值這兩種模式揣炕。

  • AT_MOST(最大模式)
    父容器指定了一個(gè)可用大小即就是低30位的SpecSize的值帘皿,最終View的值不能大于這個(gè)值。它對(duì)應(yīng)于LayoutParmas中的wrap_content畸陡。

MeasureSpec和LayoutParams的對(duì)應(yīng)關(guān)系

Android系統(tǒng)內(nèi)部是通過(guò)MeasureSpec來(lái)進(jìn)行View測(cè)量的鹰溜,但正常情況下我們使用View指定MeasureSpec;同時(shí)我們也可以給View設(shè)置LayoutParams丁恭。在View的測(cè)量時(shí)曹动,系統(tǒng)會(huì)將LayoutParams在父容器的約束下轉(zhuǎn)換成對(duì)應(yīng)的MeasureSpec,然后在根據(jù)MeasureSpec來(lái)確定View測(cè)量后的寬高牲览。

  • MeasureSpec不是僅由LayoutParams決定的墓陈,其需要和父容器一起才能決定View的MeasureSpec,從而進(jìn)一步?jīng)Q定View的寬高。
  • 頂級(jí)View(DecorView)的MeasureSepc由窗口的尺寸和其自身的LayoutParams來(lái)共同決定第献。
  • 對(duì)于普通View,它的MeasureSpec由父容器的MeasureSpec和其自身的LayoutParams來(lái)共同決定贡必。
  • MeasureSpec一旦確定onMeasure中就可以確定View的測(cè)量寬、高庸毫。

DecorView中對(duì)應(yīng)關(guān)系

  1. LayoutParams.MATCH_PARENT:精確模式大小就是窗口的大小沟于。
  2. LayoutParams.WRAP_CONTENT:最大模式尽纽,大小不確定,但不能超過(guò)窗口的大小
  3. 固定模式(如,100dp):精確模式来惧,大小為L(zhǎng)ayoutParams指定的大小四苇。

普通View中的對(duì)應(yīng)關(guān)系

  1. 當(dāng)View采用固定模式寬高的時(shí)候盗冷,不管父容器的MeasureSpec是什么荔烧,View的MeasureSpec是精確模式且其大小為當(dāng)前View要求的大小。
  2. 當(dāng)View寬高采用match_parent時(shí)刚盈,那么View的MeasureSpec是精確模式且其大小為父容器剩余的空間大邢勐濉;如果父容器為最大模式,那么View的MeasureSpec是最大模式且其大小不超過(guò)父容器剩余的空間大小欲侮。
  3. 當(dāng)View的寬高采用wrap_content時(shí)崭闲,不論父容器的模式是精確模式還是最大模式,View的MeasureSpec是最大模式且其大小不超過(guò)父容器剩余的空間大小威蕉。
普通 View 的 MeasureSpec的創(chuàng)建規(guī)則
public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
        int specMode = MeasureSpec.getMode(spec);
        int specSize = MeasureSpec.getSize(spec);

        int size = Math.max(0, specSize - padding);

        int resultSize = 0;
        int resultMode = 0;

        switch (specMode) {
        // Parent has imposed an exact size on us
        case MeasureSpec.EXACTLY:
            if (childDimension >= 0) {
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.MATCH_PARENT) {
                // Child wants to be our size. So be it.
                resultSize = size;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                // Child wants to determine its own size. It can't be
                // bigger than us.
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            }
            break;

        // Parent has imposed a maximum size on us
        case MeasureSpec.AT_MOST:
            if (childDimension >= 0) {
                // Child wants a specific size... so be it
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.MATCH_PARENT) {
                // Child wants to be our size, but our size is not fixed.
                // Constrain child to not be bigger than us.
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                // Child wants to determine its own size. It can't be
                // bigger than us.
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            }
            break;

        // Parent asked to see how big we want to be
        case MeasureSpec.UNSPECIFIED:
            if (childDimension >= 0) {
                // Child wants a specific size... let him have it
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.MATCH_PARENT) {
                // Child wants to be our size... find out how big it should
                // be
                resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
                resultMode = MeasureSpec.UNSPECIFIED;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                // Child wants to determine its own size.... find out how
                // big it should be
                resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
                resultMode = MeasureSpec.UNSPECIFIED;
            }
            break;
        }
        //noinspection ResourceType
        return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
    }

View的工作流程

View的工作流程主要是指measure(測(cè)量)刁俭、layout(布局)、draw(繪制)這三大流程韧涨。
其中measure確定了View的測(cè)量寬高牍戚,layout確定了View的最終寬高和四個(gè)頂點(diǎn)的位置,draw則將View繪制到屏幕上虑粥。

measure過(guò)程

measure過(guò)程要分兩種情況來(lái)分析如孝。如果是原始的View,那么通過(guò)measure方法就完成了View的測(cè)量過(guò)程娩贷;如果是一個(gè)ViewGroup除了完成自己的測(cè)量過(guò)程外第晰,還要遍歷調(diào)用所有子元素的measure方法,各個(gè)子元素再遞歸的執(zhí)行這個(gè)流程彬祖。

原始View的measure過(guò)程

View的measure過(guò)程使用measure方法完成的茁瘦,measure方法是一個(gè)final修飾的方法,意味著子類不能重寫(xiě)此方法储笑,在measure方法中會(huì)調(diào)用其onMeasure方法甜熔。onMeasure方法源碼如下:

    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
                getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
    }
    
    public static int getDefaultSize(int size, int measureSpec) {
        int result = size;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);

        switch (specMode) {
        case MeasureSpec.UNSPECIFIED:
            result = size;
            break;
        case MeasureSpec.AT_MOST:
        case MeasureSpec.EXACTLY:
            result = specSize;
            break;
        }
        return result;
    }

onMeasure方法很簡(jiǎn)潔,setMeasuredDimension方法會(huì)設(shè)置View的寬高的測(cè)量值突倍。我們同時(shí)需要關(guān)注getDefaultSize這個(gè)方法纺非,這個(gè)方法邏輯很簡(jiǎn)單在AT_MOST和EXACTLY這兩種情形下,getDefaultSize大小就是MeasureSpec的specSize的值赘方。

而在UNSPECIFIED(位指明模式)的情形下,一般用于系統(tǒng)內(nèi)部的測(cè)量過(guò)程弱左,getSuggestedMinimumWidth窄陡、getSuggestedMinimumHeight這兩個(gè)方法的返回值。源碼如下:

   protected int getSuggestedMinimumHeight() {
        return (mBackground == null) ? mMinHeight : max(mMinHeight, mBackground.getMinimumHeight());
   }

   protected int getSuggestedMinimumWidth() {
        return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());
   }

這種情形下拆火,可以得出如下結(jié)論:
如果View沒(méi)有設(shè)置背景跳夭,那么返回android:minWidth這個(gè)屬性所指定的值,這個(gè)值可以為0们镜;如果View設(shè)置了背景币叹,則返回minWidth和背景的最小寬度、高度之間的最大值模狭。

結(jié)論

直接繼承View的自定義空間需要重寫(xiě)onMeasure方法颈抚,并設(shè)置wrap_content時(shí)的自身大小,否則在布局中使用wrap_content就相當(dāng)于使用了match_parent嚼鹉。

View在布局中使用wrap_content,那么他的specMode為AT_MOST,這種模式下寬高等于specSize,根據(jù)之前的表格可以得知,specSize就是parentSize即父容器當(dāng)前剩余的空間大小;很顯然這種效果等同于布局中使用match_parent贩汉。

如何解決這個(gè)問(wèn)題?很簡(jiǎn)單驱富,只需要給View指定一個(gè)默認(rèn)的內(nèi)部寬高,并在wrap_content是設(shè)置此寬高即可匹舞。代碼如下:

    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
        heightMeasureSpec);
        if (widthSpecMode == MeasureSpec.AT_ MOST && heightSpecMode == MeasureSpec.AT_ MOST){
            setMeasuredDimension(mWidth, mHeight);
        } else if (widthSpecMode == MeasureSpec.AT_ MOST){
            setMeasuredDimension(mWidth, heightSpecSize);
        } else if (heightSpecMode == MeasureSpec.AT_ MOST){
            setMeasuredDimension(widthSpecSize, mHeight);
        }
    }

查看TextView褐鸥、ImageView等的源碼可以知道,針對(duì)wrap_content的情形赐稽,在onMeasure方法中都做了特殊處理叫榕,可以自行閱讀查看。

ViewGroup的measure過(guò)程

和View不同的是ViewGroup是一個(gè)抽象類姊舵,因此它沒(méi)有重寫(xiě)onMeasure方法晰绎,而是提供了一個(gè)叫做measureChildren的方法,如下所示蠢莺。

    protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) {
        final int size = mChildrenCount;
        final View[] children = mChildren;
        for (int i = 0; i < size; ++i) {
            final View child = children[i];
            if ((child.mViewFlags & VISIBILITY_MASK) != GONE) {
                measureChild(child, widthMeasureSpec, heightMeasureSpec);
            }
        }
    }

    protected void measureChild(View child, int parentWidthMeasureSpec,
            int parentHeightMeasureSpec) {
        final LayoutParams lp = child.getLayoutParams();

        final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
                mPaddingLeft + mPaddingRight, lp.width);
        final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
                mPaddingTop + mPaddingBottom, lp.height);

        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    }

上述代碼可以看出ViewGroup在measure時(shí)寒匙,會(huì)對(duì)每一個(gè)子元素進(jìn)行measure,measureChild方法的實(shí)現(xiàn)就是取出子元素的LayoutParams躏将,然后再通過(guò)getChildMeasureSpec來(lái)獲取子元素的MeasureSpec,接著講MeasureSpec傳遞給依次調(diào)用子元素的measure方法進(jìn)行測(cè)量锄弱。

總結(jié)

ViewGroup作為一個(gè)抽象類并沒(méi)有定義其測(cè)量的具體過(guò)程,代表測(cè)量過(guò)程的onMeasure方法需要各個(gè)子類進(jìn)行具體的實(shí)現(xiàn)祸憋,比如LinearLayout会宪、RelativeLayout等。ViewGroup的實(shí)現(xiàn)類有不同的布局特性蚯窥,導(dǎo)致系統(tǒng)無(wú)法統(tǒng)一實(shí)現(xiàn)掸鹅,因此才決定交給擴(kuò)展類區(qū)實(shí)現(xiàn)。


View的測(cè)量過(guò)程是三大流程中最復(fù)雜的一個(gè)拦赠,measure完成以后巍沙,通過(guò)getMeasureWidth/Height方法就可以正確獲取到View的測(cè)量寬、高荷鼠。需要注意的是句携,極端的情況下,系統(tǒng)可能需要多次調(diào)用measure才能最終確定測(cè)量寬允乐、高矮嫉,這種情形下onMeasure拿到的測(cè)量寬高可能并不準(zhǔn)確。一個(gè)比較好的習(xí)慣是在onLayout方法中獲取View的測(cè)量寬高牍疏,或者最終寬高蠢笋。

參考一:《安卓開(kāi)發(fā)藝術(shù)探究》實(shí)體書(shū)

參考二:《安卓開(kāi)發(fā)藝術(shù)探究》Kindle電紙書(shū)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市鳞陨,隨后出現(xiàn)的幾起案子昨寞,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件编矾,死亡現(xiàn)場(chǎng)離奇詭異熟史,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)窄俏,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)蹂匹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人凹蜈,你說(shuō)我怎么就攤上這事限寞。” “怎么了仰坦?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵履植,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我悄晃,道長(zhǎng)玫霎,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任妈橄,我火速辦了婚禮庶近,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘眷蚓。我一直安慰自己鼻种,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布沙热。 她就那樣靜靜地躺著叉钥,像睡著了一般。 火紅的嫁衣襯著肌膚如雪篙贸。 梳的紋絲不亂的頭發(fā)上投队,一...
    開(kāi)封第一講書(shū)人閱讀 51,631評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音爵川,去河邊找鬼敷鸦。 笑死,一個(gè)胖子當(dāng)著我的面吹牛雁芙,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播钞螟,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼兔甘,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了鳞滨?” 一聲冷哼從身側(cè)響起洞焙,我...
    開(kāi)封第一講書(shū)人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后澡匪,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體熔任,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年唁情,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了疑苔。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡甸鸟,死狀恐怖惦费,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情抢韭,我是刑警寧澤薪贫,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站刻恭,受9級(jí)特大地震影響瞧省,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜鳍贾,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一鞍匾、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧贾漏,春花似錦候学、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至伍掀,卻和暖如春掰茶,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蜜笤。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工濒蒋, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人把兔。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓沪伙,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親县好。 傳聞我的和親對(duì)象是個(gè)殘疾皇子围橡,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

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