Android View的Measure過程

大家都知道Android View繪制過程包含Measure、Layout、Draw三個主要的過程,這個過程看似簡單,但是在應(yīng)用的時候,很多同學(xué)還是不能很好的運(yùn)用。我希望這篇文章可以把其中的一部分——Measure——講的更加清晰一點(diǎn)。

Measure過程是對View大小的測量過程,相比其他兩個過程,Measure的邏輯更加復(fù)雜。Measure過程是RootView調(diào)用performTraversals()方法時執(zhí)行的。我們只關(guān)心“看的到”的部分。Measure的過程由View樹上的View在onMeasure方法中調(diào)用子View的measure方法完成的。有點(diǎn)繞,不過,對于自定義View或者自定義ViewGroup來說,我們需要關(guān)心下面的內(nèi)容:

  • 自定義View:覆寫onMeasure方法,計(jì)算合適的大小,并將結(jié)果通過setMeasuredDimension()方法保存結(jié)果。
  • 自定義ViewGroup:除了完成上面所說的工作外,還需要調(diào)用子View的measure方法,確保每個子View都正確的測量。

自定義View

先貼一個算是通用的自定義View onMeasure方法實(shí)現(xiàn):

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            setMeasuredDimension(measure(widthMeasureSpec, true), measure(heightMeasureSpec, false));
    }

    protected int measure(int measureSpec, boolean WOH) {
        int size = MeasureSpec.getSize(measureSpec);
        int mode = MeasureSpec.getMode(measureSpec);

        int measured;
        if (mode == MeasureSpec.EXACTLY) {
            measured = size;
        } else {
            int measureMinimum = WOH ? getMinimumMeasureWidth() : getMinimumMeasureHeight();

          // 根據(jù)內(nèi)容計(jì)算最小值
          // measureMinimum = Math.max(measureMinimum, MIN_CONTENT_SIZE);

            if (WOH) {
                measureMinimum = Math.max(measureMinimum, measureMinimum + getPaddingLeft() + getPaddingRight());
            } else {
                measureMinimum = Math.max(measureMinimum, measureMinimum + getPaddingTop() + getPaddingBottom());
            }
            measured = measureMinimum;
            if (mode == MeasureSpec.AT_MOST) {
                measured = Math.min(measured, size);
            }
        }
        return measured;
    }

上面的代碼對于繪制類的自定義View(主要作用在于展示更豐富的圖形樣式,而不在于布局)比較實(shí)用,以上代碼計(jì)算大小的步驟:

  1. 先取View的期望的最小寬/高,這個最小值由View的內(nèi)容和設(shè)置決定。

    什么是期望的最小寬/高?

    View的大小的應(yīng)該至少滿足內(nèi)容的顯示需求,比如要顯示一個10個漢字的View,那么這個View的期望最小寬/高就是“當(dāng)前文字樣式下10個漢字的寬/高 + padding”。

  2. 根據(jù)MeasureSpec的模式,確定最終的寬/高。具體邏輯是:

    1. MeasureSpec.EXACTLY:以MeasureSpec的size為準(zhǔn)。
    2. MeasureSpec.AT_MOST:取期望和MeasureSpec的size的最小值。
    3. MeasureSpec.UNSPECIFIED:取期望值。

    MeasureSpec的三種模式,下面還會專門說明。所以暫時先不要糾結(jié)上面邏輯的理由。

  3. 調(diào)用setMeasuredDimension()方法保存結(jié)果。

總結(jié)

自定義View的Measure過程通用處理方法:首先要確定View需要的(顯示內(nèi)容)最小/合適大小,然后根據(jù)MeasureSpec的三種模式確定最終的measured尺寸。

MeasureSpec

之所以沒有開始就講這個類,是因?yàn)樵谥v之前我希望大家先對自定義View的Measure過程有個印象。

定義:MeasureSpec封裝了父View對子View的布局需求。所以這個類表示了一種需求需求需求

MeasureSpec由modesize兩個部分組成,這兩個部分通過位計(jì)算儲存到一個int類型中,(怎么個結(jié)構(gòu)這里就不細(xì)說了,看源碼吧),通過getMode()getSize()獲取。這兩個方法加上構(gòu)造方法基本就是MeasureSpec的全部API了。

size很好理解,下面把三種mode翻譯成普通話:(以下“我”代表父View,“你”代表子View,“size”表示MeasureSpec的size)

  1. MeasureSpec.EXACTLY:我需要你的大小和size一樣。
  2. MeasureSpec.AT_MOST:你可以是(根據(jù)內(nèi)容確定的或是)任意大小,但是不能超過size。
  3. MeasureSpec.UNSPECIFIED:你可以是(根據(jù)內(nèi)容確定的或是)任意大小。

以上“我”代表父View,“你”代表子View,“size”表示MeasureSpec的size。

對于子View來說,在onMeasure方法中拿到MeasureSpec之后,就要根據(jù)自己的期望和MeasureSpec的需求確定最終大小。而且一般情況下,對于繪制類的自定義View,通過第一節(jié)的方法都可以完成Measure過程。

對于父View來說,首先它也是“爺爺View”的子View,所以也是要在onMeasure方法中處理,拿到MeasureSpec之后,不僅要通過自己的期望和“爺爺View”的需求確定大小,還要負(fù)責(zé)子View的measure過程,它需要(在自己的onMeasure方法中

  • 通過調(diào)用子View的View.measure(int, int)方法向子View傳遞自己的合適的需求。
  • 通過調(diào)用子View的View.measure(int, int)方法向子View傳遞自己的合適的需求
  • 通過調(diào)用子View的View.measure(int, int)方法向子View傳遞自己的合適的需求。
  • 通過調(diào)用子View的View.measure(int, int)方法向子View傳遞自己的合適的需求。

具體父View應(yīng)該怎么做,請繼續(xù)往下看。

總結(jié)

MeasureSpec表是父View對子View的measure需求。對于自定義View來說,需要在onMeasure中考慮MeasureSpec的值,從而確定最終measured尺寸;對于自定義ViewGroup而言,還需要通過調(diào)用子View的View.measure(int, int)方法向子View傳遞自己的合適的需求

LayoutParams

在進(jìn)入自定義ViewGroup的Measure過程之前,還需要考慮一個因素。LayoutParams,直譯過來就是“布局參數(shù)”。上面講了MeasureSpec是父View傳遞給子View的需求,而LayoutParams,是子View的布局參數(shù)。作用是什么呢?向父View傳遞需求。兩個需求是有差別的,一般對于子View來說,只需要關(guān)心MeasureSpec,而LayoutParams是ViewGroup需要考慮的因素。這也是為什么在這里討論LayoutParams的原因。

LayoutParams(這里指ViewGroup.LayoutParams)相比MeasureSpec更加簡單,封裝了兩個值,width和height。這兩個值是開發(fā)者對子View大小的約束,對ViewGroup來說,這兩個值表示“子View希望ViewGroup如何Measure自己”。覺得暈沒關(guān)系,繼續(xù)往下看。

width和height的取值類型一致,共有三種:

  1. MATCH_PARENT (-1): 表示子View希望自己和父控件的width/height一致。一般情況下,會導(dǎo)致onMeasure方法中得到mode為EXACTLY,size為父View寬/高的MeasureSpec。
  2. WRAP_CONTENT (-2): 表示子View希望自己的width和height由自己的內(nèi)容決定。一般情況下,會導(dǎo)致onMeasure方法中得到mode為AT_MOST,size為父View寬/高的MeasureSpec。
  3. 任意非負(fù)整數(shù): 表示子View希望自己的width和height是確切的這個值。一般情況下,會導(dǎo)致onMeasure方法中得到mode為EXACTLY,size為該值的MeasureSpec。

對于ViewGroup來說,LayoutParams的取值表達(dá)了子View對自己width和height的期望。

Q: Android View的size不是View的onMeasure確定的嗎?為什么要向父View傳遞期望?

A: 第一節(jié)有提到,View的onMeasure方法要根據(jù)onMeasure的參數(shù)(兩個MeasureSpec)最終確定。而在ViewGroup知道View的類型之前,是不知道如何向子View傳遞MeasureSpec的(ViewGroup也是很講道理的,MeasureSpec表達(dá)了ViewGroup對子View的measure期望,但也不能隨便傳啊。)。LayoutParams就是ViewGroup確定向子View傳遞怎樣的MeasureSpec的確定因素之一。

ViewGroup根據(jù)LayoutParams的width/height和自己的設(shè)計(jì)(每個特定的ViewGroup類型,比如LinearLayout、FrameLayout)來確定向子類傳遞的MeasureSpec。

注意:這里說的是【LayoutParams的width/height】而不是【LayoutParams】,因?yàn)樘囟ǖ腣iewGroup是可以自己定義屬于自己的LayoutParams的,比如LinearLayout.LayoutParams定義了gravity,RelativeLayout定義了toLeftOf、above等特定的布局參數(shù),這些參數(shù)在ViewGroup的onMeasure方法內(nèi)也都會考慮到,但總的來說還是width/height在起作用,尤其是對于大部分自定義ViewGroup來說。舉個例子:

RelatIveLayout里面的子View,即便將LayoutParams.width設(shè)置為WRAP_CONTENT,但是如果同時將這個子View的alignParentLeft、alignParentRight設(shè)置為true的話,子View在onMeasure里面拿到的widthMeasureSpec的mode依然是EXACTLY。

因?yàn)镽elatIveLayout根據(jù)以上兩個alignParentLeft/Right屬性判斷,這個子View是希望MATCH_PARENT的。

總結(jié)

LayoutParams表示子View對自己布局(包含measure和layout)的期望,ViewGroup.LayoutParams僅包含width和height兩個值。ViewGroup在確定自己對某個子View的MeasureSpec時,一般需要考慮這個子View的LayoutParams參數(shù)。

自定義ViewGroup

如果自定義ViewGroup是繼承自Framework內(nèi)的幾個Layout類,那么Measure過程大部分情況下不需要關(guān)心。因?yàn)椋?/p>

  1. 如果自定義ViewGroup的目的是為了自定義自View的布局規(guī)則,那么請直接繼承ViewGroup類。
  2. 如果自定義ViewGroup的目的是為了包裝業(yè)務(wù),那么不需要涉及布局規(guī)則的定義,就不需要關(guān)心Measure和Layout過程了。
  3. 如果兩者都有,那么參見第一條。

這里我們討論直接繼承自ViewGroup的情況。根據(jù)剛才的結(jié)論,自定義ViewGroup類,自然是要干涉子View的布局邏輯。比如:按比例布局、按某種圖形布局、自動折行等等。

自定義ViewGroup就是上面討論的“父View”,所以它需要:

  • 通過調(diào)用子View的View.measure(int, int)方法向子View傳遞自己的合適的需求

這句話是第6次出現(xiàn)了,這很重要。

你至少應(yīng)該從中得到以下信息:(以下VG表示“自定義ViewGroup”)

  1. 自定義ViewGroup要在onMeasure方法中調(diào)用所有需要布局的子View(有些View,比如不需要顯示,可以不測量)的measure方法。
  2. 自定義ViewGroup要向子View傳遞Measure需求。
  3. 自定義ViewGroup向子View傳遞的MeasureSpec是代表自己對子View的Measure需求,可以并且一般也都和onMeasure方法的參數(shù)(“爺爺View的需求”)的MeasureSpec不同。
  4. 自定義ViewGroup在確定MeasureSpec時,要考慮到子View的LayoutParams參數(shù),從而確定合適的MeasureSpec。

所以自定義ViewGroup的Measure過程的關(guān)鍵就是向子View傳遞合適的需求,就是對每個子View構(gòu)建合適的MeasureSpec。

以FrameLayout為例

還是很抽象對嗎?讓我們來看一下Framework內(nèi)置的Layout是怎么做的。這里討論FrameLayout,因?yàn)镕rameLayout的measure過程相對簡單,不至于跑題。當(dāng)然,這個過程也是可以覆蓋剛才我們討論的整個過程和原理的(事實(shí)上,上面討論的原理是普適性的)。如果你有興趣可以再繼續(xù)研究下其他Layout的measure過程,我認(rèn)同 Read the ** source code. 是最有效最基礎(chǔ)的學(xué)習(xí)方法。

源碼就不貼了,太占地方,下面要和大家一起分析的是版本號為23的SDK中的源碼,可以打開AndroidStudio對照著看。

onMeasure步驟

  1. 對每個View進(jìn)行measure,調(diào)用ViewGroup.measureChildWithMargin方法。這里傳入的MeasureSpec是使用ViewGroup的默認(rèn)實(shí)現(xiàn)計(jì)算(注1)得到的。同時記錄所有子View的width/height的最大值。

  2. 取子View的最大width/height,考慮自己的minHeight/minWidth、Foreground和Padding,得到新的最大值,作為自己的暫時measuredWidth/measuredHeight。

  3. 結(jié)合onMeasure的參數(shù)MeasureSpec(父ViewGroup的measure需求),得到最終measuredWidth、measuredHeight,調(diào)用setMeasuredDimension方法。(measuredState見注2)

  4. 判斷:如果onMeasure參數(shù)中有非EXACTLY mode的MeasureSpec(某個方向或者某兩個方向尺寸不確定),并且子View的LayoutParams中,有MATCH_PARENT的值。如果不滿足,結(jié)束onMeasure;否則繼續(xù)。

  5. 對LayoutParams中有MATCH_PARENT的值的View重新measure。對設(shè)置了MATCH_PARENT值的這個方向,使用經(jīng)第1、2、3步驟處理后最終確定的自己(FrameLayout)的width/height作為size,EXACTLY作為mode的MeasureSpec。

    因?yàn)榈?步measure子View的時候,沒有考慮到1、2、3步驟之后最終確定的自己的大小,所以對于設(shè)置了MATCH_PARENT的View,無法給出確切的值,所以要再次調(diào)用子View的measure方法,傳入正確的值。

注1:ViewGroup.getChildMeasureSpec方法。根據(jù)從父ViewGroup獲取到的MeasureSpec和子View的LayoutParams,得到合適的MeasureSpec。

注2:關(guān)于measuredState,目前應(yīng)用很狹窄,暫時可以忽略。

它的場景只有一種,涉及到的值常量也只有一個:MEASURED_STATE_TOO_SMALL。表示measure過程中最終確定的size小于measure過程中計(jì)算得到的需要的(內(nèi)容)size。

在上面的步驟中,上述第3步中會調(diào)用FrameLayout的View.resolveSizeAndState方法,如果暫時的measuredWidth/measuredHeight小于父ViewGroup提供的MeasureSpec的size并且MeasureSpec的mode為AT_MOST的話,將在最終得到的measuredSize的高8位保存MEASURED_STATE_TOO_SMALL(0x01000000)。

舉個例子:

RelativeLayout > FrameLayout > View三層布局,F(xiàn)rameLayout的LayoutParams為WRAP_CONTENT,里面View的寬高設(shè)為超過RelativeLayout的值,F(xiàn)rameLayout的measureState就包含MEASURED_STATE_TOO_SMALL。

總結(jié)

自定義ViewGroup的measure過程除了要確定自身的measuredSize;同時要向子View傳遞合適的MeasureSpec,保證子View正確measure,在確定MeasureSpec時,通常要考慮到每個子View的LayoutParams。

實(shí)例分析

理論是要結(jié)合實(shí)踐的,下面通過兩個實(shí)例,來分別分析下自定義View和自定義ViewGroup的measure。

自定義View——FixRatioImageView

FixRatioImageView繼承自Image,作用是根據(jù)image source的比例確定View的大小,要求有一邊為EXCATLY(MATCH_PARENT或固定數(shù)值)。開發(fā)中會遇到需要固定比例顯示的圖片資源,有些時候是需要有固定的布局需求的。ImageView其實(shí)已經(jīng)設(shè)計(jì)了屬性adjustViewBounds但是在第三方系統(tǒng)上的兼容性并不好,所以我們通過干涉ImageView的onMeasure方法,實(shí)現(xiàn)這個需求。

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if (mRatio == 0) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            return;
        }
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);

        int width = widthSize, height = heightSize;
        if (widthMode != MeasureSpec.EXACTLY && heightMode != MeasureSpec.EXACTLY) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            return;
        } else if (widthMode == MeasureSpec.EXACTLY) {
            height = (int) (width / mRatio + 0.5f);
        } else if (heightMode == MeasureSpec.EXACTLY) {
            width = (int) (height * mRatio + 0.5f);
        }

        setMeasuredDimension(width, height);
    }

mRatio表示固定的寬高比:width/height

上面的代碼,先判斷是否有寬或者高為EXACTLY,如果都不是,那么使用ImageView的measure邏輯,否則根據(jù)mRatio的值,計(jì)算另一邊的值。有一邊為EXCATLY這個前提不能適用所有情況,但是大部分需求都能滿足了。

使用方法如下:

<com.kyleduo.androidcustomview.view.FixRatioImageView
            android:id="@+id/fix_ratio"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:scaleType="centerCrop"
            android:src="@drawable/fixratio"
            app:fr_rate="10.56338"/>

自定義ViewGroup——內(nèi)容左對齊的KeyValueItem

不知道大家有沒有做過這種列表:

measure_custom_vg_list

每個列表項(xiàng)分為標(biāo)題和內(nèi)容,標(biāo)題和內(nèi)容都是左對齊,并且所有內(nèi)容都要左對齊。但你遇到這種列表你會想到怎么做呢?

如果你能想到通過自定義ViewGroup實(shí)現(xiàn),那一定是極好的。很明顯,對于這類需要有特殊布局要求的開發(fā)需求,自定義ViewGroup應(yīng)該是首先想到的方法。

思路是這樣的,自定義KeyValueItem,使用靜態(tài)變量儲存左側(cè)Title的最大寬度,然后measure右側(cè)Message的時候,減去左側(cè)寬度,保證正確測量。onLayout里面,右側(cè)Message layout的左邊緣在Title最大寬度右側(cè)。

是不是很清晰?嗯,還有個問題需要考慮,因?yàn)檫@個ViewGroup肯定是要復(fù)用的,而最大寬度通過靜態(tài)變量保存,那么多個頁面進(jìn)行復(fù)用的時候,就會出現(xiàn)寬度被污染的問題。分析需求,這種列表是在同一個ViewGroup下布局的,也就是說他們有一個公共的父View,那么我們就可以用父View作為Key,保存這個最大寬度,當(dāng)Key變化時對最大寬度進(jìn)行清空。直接引用父View當(dāng)然不行,我們?nèi)「竀iew對象的hashCode()作為key。

如果你向我一樣這個列表每個Activity中只出現(xiàn)一次,那么直接用Context的hashCode也可以。

這里貼出onMeasure和onLayout的源碼:

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        if (widthMode != MeasureSpec.EXACTLY) {
            throw new IllegalArgumentException("width must be exactly");
        }

        if (getParent() != null) {
            int parentHash = getParent().hashCode();
            if (parentHash != sMaxKey) {
                sMaxKey = parentHash;
                sMaxTitleWidth = 0;
            }
        }

        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        int maxTitleWidth = widthSize / 2 - space * 2;
        int maxChildHeight = 0;
        for (int i = 0; i < getChildCount(); i++) {
            int childWidth = widthSize;

            View child = getChildAt(i);
            if (child == mTitleTv) {
                childWidth = maxTitleWidth;
            } else if (child == mContentTv) {
                childWidth = widthSize - sMaxTitleWidth - space * 3; // |-[title]-space-[content]-space|
            }

            child.measure(MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.AT_MOST), heightMeasureSpec);
            if (child == mTitleTv) {
                int width = child.getMeasuredWidth();
                if (width > sMaxTitleWidth) {
                    sMaxTitleWidth = width;
                }
            }

            int height = child.getMeasuredHeight();
            if (height > maxChildHeight) {
                maxChildHeight = height;
            }
        }

        setMeasuredDimension(widthSize, Math.max(maxChildHeight + space * 2, mMinHeight));
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int centerY = getMeasuredHeight() / 2;
        int left = getPaddingLeft() + space;
        int right = getMeasuredWidth() - space;
        if (mTitleTv != null) {
            mTitleTv.layout(left, centerY - mTitleTv.getMeasuredHeight() / 2, left + mTitleTv.getMeasuredWidth(), centerY + mTitleTv.getMeasuredHeight() / 2);
            left += sMaxTitleWidth + space;
        }
        if (mContentTv != null) {
            mContentTv.layout(left, centerY - mContentTv.getMeasuredHeight() / 2, right, centerY + mContentTv.getMeasuredHeight() / 2);
        }
    }

Q: 代碼里并沒有使用for循環(huán)之類的語句遍歷子View?

A: KeyValueListItem并不是一個通用的Layout控件,里面只有兩個子View并且是確定的兩個子View,而且他們的Layout結(jié)構(gòu)也是確定的,所以可以直接針對這兩個對象進(jìn)行Layout。

如果是自定義類似標(biāo)簽云這種包含平等子View的ViewGroup,那么遍歷是必然的。

最終的效果是這樣的:

如果查看LayoutBounds,可以看到也是非常干凈。

總結(jié)

Measure過程是對View尺寸的測量過程,View通過onMeasure方法確定自己的尺寸,ViewGroup在確定自己尺寸的同時,要正確調(diào)用子View的measure()方法,讓子View正確測量。自定義View和ViewGroup的時候,也是通過onMeasure方法完成measure過程。

這篇文章分別討論了自定義View、自定義ViewGroup的measure方法,也解釋了MeasureSpec和LayoutParams的含義以及他們是如何在View的measure過程中提起作用的;然后分析了FrameLayout的onMeasure方法實(shí)現(xiàn);最后通過兩個實(shí)例分析在場景用應(yīng)用了measure過程的技術(shù)要點(diǎn)。

關(guān)于Android View的measure,就講到這里吧,希望對大家有幫助。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,363評論 6 532
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,497評論 3 416
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,305評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,962評論 1 311
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,727評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,193評論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,257評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,411評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,945評論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,777評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,978評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,519評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,216評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,642評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,878評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,657評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,960評論 2 373

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