View 繪制體系知識(shí)梳理(3) - 繪制流程之 Measure 詳解

一、測(cè)量過(guò)程的信使 - MeasureSpec

因?yàn)闇y(cè)量是一個(gè)從上到下的過(guò)程呐萌,而在這個(gè)過(guò)程當(dāng)中渗磅,父容器有必要告訴子View它的一些繪制要求,那么這時(shí)候就需要依賴一個(gè)信使按灶,來(lái)傳遞這個(gè)要求症革,它就是MeasureSpec.
MeasureSpec是一個(gè)32位的int類型,我們把它分為高2位和低30位鸯旁。
其中高2位表示mode地沮,它的取值為:

  • UNSPECIFIED(0) : The parent has not imposed any constraint on the child. It can be whatever size it wants.
  • EXACTLY(1) : The parent has determined an exact size for the child. The child is going to be given those bounds regardless of how big it wants to be.
  • AT_MOST(2) : The child can be as large as it wants up to the specified size.

30位表示具體的size

MeasureSpec是父容器傳遞給View的寬高要求羡亩,并不是說(shuō)它傳遞的size是多大摩疑,子View最終就是多大,它是根據(jù)父容器的MeasureSpec和子ViewLayoutParams共同計(jì)算出來(lái)的畏铆。

為了更好的理解上面這段話雷袋,我們需要借助ViewGroup中的兩個(gè)函數(shù):

  • measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed)
  • getChildMeasureSpec(int spec, int padding, int childDimension)
    protected void measureChildWithMargins(View child,
            int parentWidthMeasureSpec, int widthUsed,
            int parentHeightMeasureSpec, int heightUsed) {
        final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
        final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
                mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
                        + widthUsed, lp.width);
        final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
                mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
                        + heightUsed, lp.height);
        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    }

    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) {
        case MeasureSpec.EXACTLY:
            if (childDimension >= 0) {
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.MATCH_PARENT) {
                resultSize = size;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            }
            break;
        case MeasureSpec.AT_MOST:
            if (childDimension >= 0) {
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.MATCH_PARENT) {
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            }
            break;
        case MeasureSpec.UNSPECIFIED:
            if (childDimension >= 0) {
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.MATCH_PARENT) {
                resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
                resultMode = MeasureSpec.UNSPECIFIED;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
                resultMode = MeasureSpec.UNSPECIFIED;
            }
            break;
        }
        return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
    }

可以看到,在調(diào)用getChildMeasureSpec之前辞居,需要考慮parentchild之間的間距楷怒,這包括parentpaddingchildmargin,因此瓦灶,參與傳遞給childMeasureSpec的參數(shù)要考慮這么幾方面:

  • 父容器的measureSpecpadding
  • Viewheightwidht以及margin鸠删。

下面我們來(lái)分析getChildMeasureSpec的具體流程,它對(duì)寬高的處理邏輯都是相同的贼陶,根據(jù)父容器measureSpecmode刃泡,分成以下幾種情況:

1.1 父容器的modeEXACTLY

這種情況下說(shuō)明父容器的大小已經(jīng)確定了巧娱,就是固定的值。

  • View指定了大小
    那么子Viewmode就是EXACTLY烘贴,size就是布局里面的值禁添,這里就有疑問(wèn)了,View所指定的寬高大于父容器的寬高怎么辦呢桨踪?老翘,我們先留著這個(gè)疑問(wèn)。
  • ViewMATCH_PARENT
    View希望和父容器一樣大锻离,因?yàn)楦溉萜鞯拇笮∈谴_定的铺峭,所以子View的大小也是確定的,size就是父容器measureSpecsize - 父容器的padding - 子View``margin汽纠。
  • ViewWRAP_CONTENT
    子容器只要求能夠包裹自己的內(nèi)容逛薇,但是這時(shí)候它又不知道它所包裹的內(nèi)容到底是多大,那么這時(shí)候它就指定自己的大小就不能超過(guò)父容器的大小疏虫,所以modeAT_MOST永罚,size和上面類似。

1.2 父容器的modeAT_MOST

在這種情況下卧秘,父容器說(shuō)明了自己最多不能超過(guò)多大呢袱,數(shù)值在measureSpecsize當(dāng)中:

  • View指定大小
    同上分析。
  • ViewMATCH_PARENT
    View希望和父容器一樣大翅敌,而此時(shí)父容器只知道自己不能超過(guò)多大羞福,因此子View也就只能知道自己不能超過(guò)多大,所以它的modeAT_MOST蚯涮,size就是父容器measureSpecsize - 父容器的padding - 子View``margin治专。
  • ViewWRAP_CONTENT
    子容器只要求能夠包裹自己的內(nèi)容,但是這時(shí)候它又不知道它所包裹的內(nèi)容到底是多大遭顶,這時(shí)候雖然父容器沒(méi)有指定大小张峰,但是它指定了最多不能超過(guò)多少,這時(shí)候子View也不能超過(guò)這個(gè)值棒旗,所以modeAT_MOST喘批,size的計(jì)算和上面類似。

1.3 父容器的modeUNSPECIFIED

  • View指定大小
    同上分析铣揉。
  • ViewMATCH_PARENT
    View希望和父容器一樣大饶深,但是這時(shí)候父容器并沒(méi)有約束,所以子View也是沒(méi)有約束的逛拱,所以它的mode也為UNSPECIFIED敌厘,size的計(jì)算和之前一致。
  • ViewWRAP_CONTENT
    View不知道它包裹的內(nèi)容多大朽合,并且父容器是沒(méi)有約束的俱两,那么也只能為UNSPECIFIED了饱狂,size的計(jì)算和之前一致。

二锋华、測(cè)量過(guò)程的起點(diǎn) - performTraversals()

介紹完了基礎(chǔ)的知識(shí)嗡官,我們來(lái)從起點(diǎn)來(lái)整個(gè)看一下從View樹(shù)的根節(jié)點(diǎn)到葉節(jié)點(diǎn)的整個(gè)測(cè)量的過(guò)程箭窜。
我們先直接說(shuō)明結(jié)論毯焕,整個(gè)測(cè)量的起點(diǎn)是在ViewRootImplperformTraversals()當(dāng)中

private void performTraversals() {
        ......
        int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
        int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
        //...
        mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}

上面的mView是通過(guò)setView(View view, WindowManager.LayoutParams attrs, View panelParentView)傳進(jìn)來(lái)的,那么這個(gè)view是什么時(shí)候傳遞進(jìn)來(lái)的呢磺樱?
現(xiàn)在回憶一下纳猫,在ActivityThreadhandleResumeActivity中,我們調(diào)用了ViewManager.add(mDecorView, xxx)竹捉,而這個(gè)方法最終會(huì)調(diào)用到WindowManagerGlobal的下面這個(gè)方法:

    public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) {
            root = new ViewRootImpl(view.getContext(), display);
        }
        //....
        root.setView(view, wparams, panelParentView);
    }

也就是說(shuō)芜辕,上面的mView也就是我們?cè)?code>setContentView當(dāng)中渲染出來(lái)的mDecorView,也就是說(shuō)它是整個(gè)View樹(shù)的根節(jié)點(diǎn)块差,因?yàn)?code>mDecorView是一個(gè)FrameLayout侵续,所以它調(diào)用的是FrameLayoutmeasure方法。
那么這整個(gè)從根節(jié)點(diǎn)遍歷完整個(gè)View樹(shù)的過(guò)程是怎么實(shí)現(xiàn)的呢憨闰?
它其實(shí)就是依賴于measureonMeasure

  • 對(duì)于View状蜗,measure是在它里面定義的,而且它是一個(gè)final方法鹉动,因此它的所有子類都沒(méi)有辦法重寫該方法轧坎,在該方法當(dāng)中,會(huì)調(diào)用onMeasure來(lái)設(shè)置最終測(cè)量的結(jié)果泽示,對(duì)于View來(lái)說(shuō)开仰,它只是簡(jiǎn)單的取出父容器傳進(jìn)來(lái)的要求來(lái)設(shè)置欧募,并沒(méi)有復(fù)雜的邏輯。
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
        boolean optical = isLayoutModeOptical(this);
        if (optical != isLayoutModeOptical(mParent)) {
            Insets insets = getOpticalInsets();
            int oWidth  = insets.left + insets.right;
            int oHeight = insets.top  + insets.bottom;
            widthMeasureSpec  = MeasureSpec.adjust(widthMeasureSpec,  optical ? -oWidth  : oWidth);
            heightMeasureSpec = MeasureSpec.adjust(heightMeasureSpec, optical ? -oHeight : oHeight);
        }

        // Suppress sign extension for the low bytes
        long key = (long) widthMeasureSpec << 32 | (long) heightMeasureSpec & 0xffffffffL;
        if (mMeasureCache == null) mMeasureCache = new LongSparseLongArray(2);

        if ((mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT ||
                widthMeasureSpec != mOldWidthMeasureSpec ||
                heightMeasureSpec != mOldHeightMeasureSpec) {

            // first clears the measured dimension flag
            mPrivateFlags &= ~PFLAG_MEASURED_DIMENSION_SET;

            resolveRtlPropertiesIfNeeded();

            int cacheIndex = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT ? -1 :
                    mMeasureCache.indexOfKey(key);
            if (cacheIndex < 0 || sIgnoreMeasureCache) {
                // measure ourselves, this should set the measured dimension flag back
                onMeasure(widthMeasureSpec, heightMeasureSpec);
                mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
            } else {
                long value = mMeasureCache.valueAt(cacheIndex);
                // Casting a long to int drops the high 32 bits, no mask needed
                setMeasuredDimensionRaw((int) (value >> 32), (int) value);
                mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
            }

            // flag not set, setMeasuredDimension() was not invoked, we raise
            // an exception to warn the developer
            if ((mPrivateFlags & PFLAG_MEASURED_DIMENSION_SET) != PFLAG_MEASURED_DIMENSION_SET) {
                throw new IllegalStateException("View with id " + getId() + ": "
                        + getClass().getName() + "#onMeasure() did not set the"
                        + " measured dimension by calling"
                        + " setMeasuredDimension()");
            }

            mPrivateFlags |= PFLAG_LAYOUT_REQUIRED;
        }

        mOldWidthMeasureSpec = widthMeasureSpec;
        mOldHeightMeasureSpec = heightMeasureSpec;

        mMeasureCache.put(key, ((long) mMeasuredWidth) << 32 |
                (long) mMeasuredHeight & 0xffffffffL); // suppress sign extension
    }
  • 對(duì)于ViewGroup,由于它是View的子類奶稠,因此它不可能重寫measure方法,并且它也沒(méi)有重寫onMeasure方法钠导。
  • 對(duì)于繼承于View的控件种柑,例如TextView,它會(huì)重寫onMeasure定欧,與View#onMeasure不同的是渔呵,它會(huì)考慮更多的情況來(lái)決定最終的測(cè)量結(jié)果。
  • 對(duì)于繼承于ViewGroup的控件砍鸠,例如FrameLayout扩氢,它同樣會(huì)重寫onMeasure方法,與繼承于View的控件不同的是爷辱,由于ViewGroup可能會(huì)有子View录豺,因此它在設(shè)置自己最終的測(cè)量結(jié)果之前朦肘,還有一個(gè)重要的任務(wù):調(diào)用子Viewmeasure方法,來(lái)對(duì)子View進(jìn)行測(cè)量双饥,并根據(jù)子View的結(jié)果來(lái)決定自己的大小媒抠。

因此,整個(gè)從上到下的測(cè)量咏花,其實(shí)就是一個(gè)View樹(shù)節(jié)點(diǎn)的遍歷過(guò)程趴生,每個(gè)節(jié)點(diǎn)的onMeasure返回時(shí),就標(biāo)志它的測(cè)量結(jié)束了昏翰,而這整個(gè)的過(guò)程是以Viewmeasure方法為紐帶的:

  • 整個(gè)過(guò)程的起點(diǎn)是mDecorView這個(gè)根節(jié)點(diǎn)的measure方法苍匆,也就是performTraversals中的那句話。
  • 如果節(jié)點(diǎn)有子節(jié)點(diǎn)棚菊,也就是說(shuō)它是繼承于ViewGroup的控件浸踩,那么在它的onMeasure方法中,它并不會(huì)直接調(diào)用子節(jié)點(diǎn)的onMeasure方法统求,而是通過(guò)調(diào)用子節(jié)點(diǎn)measure方法检碗,由于子節(jié)點(diǎn)不可能重寫View#measure方法,因此它最終是通過(guò)View#measure來(lái)調(diào)用子節(jié)點(diǎn)重寫的onMeasure來(lái)進(jìn)行測(cè)量码邻,子節(jié)點(diǎn)再在其中進(jìn)行響應(yīng)的邏輯處理折剃。
  • 如果節(jié)點(diǎn)沒(méi)有子節(jié)點(diǎn),那么當(dāng)它的onMeausre方法被調(diào)用時(shí)冒滩,它需要設(shè)置好自己的測(cè)量結(jié)果就行了微驶。

對(duì)于measureonMeasure的區(qū)別,我們可以用一句簡(jiǎn)單的話來(lái)總結(jié)一下:measure負(fù)責(zé)進(jìn)行測(cè)量的傳遞开睡,onMeasure負(fù)責(zé)測(cè)量的具體實(shí)現(xiàn)因苹。

三、測(cè)量過(guò)程的終點(diǎn) - onMeasure當(dāng)中的setMeasuredDimension

上面我們講到設(shè)置的測(cè)量結(jié)果篇恒,其實(shí)測(cè)量過(guò)程的最終目的是:通過(guò)調(diào)用setMeasuredDimension方法來(lái)給mMeasureHeightmMeasureWidth賦值扶檐。
只要上面這個(gè)過(guò)程完成了,那么該ViewGroup/View/及其實(shí)現(xiàn)類的測(cè)量也就結(jié)束了胁艰,而setMeasuredDimension必須在onMeasure當(dāng)中調(diào)用款筑,否則會(huì)拋出異常,所以我們觀察所有繼承于ViewGroup/View的控件腾么,都會(huì)發(fā)現(xiàn)它們最后都是調(diào)用上面說(shuō)的那個(gè)方法奈梳。
前面我們已經(jīng)分析過(guò),measure只是傳遞的紐帶解虱,因此它的邏輯是固定的攘须,我們直接看各個(gè)類的onMeasure方法就好。

3.1 ViewonMeasure

    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;
    }

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

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

這里殴泰,我們會(huì)根據(jù)前面所說(shuō)的于宙,父容器傳遞進(jìn)來(lái)measureSpec中的mode來(lái)給這兩個(gè)變量賦值:

  • 如果modeUNSPECIFIED浮驳,那么說(shuō)明父容器并不指望多個(gè),因此子View根據(jù)自己的背景或者minHeight/minWidth屬性來(lái)給自己賦值捞魁。
  • 如果是AT_MOST或者EXACTLY至会,那么就把它設(shè)置為父容器指定的size

3.2 ViewGrouponMeasure

由于ViewGroup的目的是為了容納各子View谱俭,但是它并不確定子View應(yīng)當(dāng)如何排列奉件,也就不知道該如何測(cè)量自己,因此它的onMeasure是沒(méi)有任何意義的旺上,所以并沒(méi)有重寫瓶蚂,而是應(yīng)當(dāng)由繼承于它的控件來(lái)重寫該方法糖埋。

3.3 繼承于ViewGroup控件的onMeasure

為了方面宣吱,我們以DecorView為例,經(jīng)過(guò)前面的分析瞳别,我們知道當(dāng)我們?cè)?code>performTraversals中調(diào)用它的measure方法時(shí)征候,最終會(huì)回調(diào)到它對(duì)應(yīng)的控件類型,也就是FrameLayoutonMeasure方法:

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int count = getChildCount();

        final boolean measureMatchParentChildren =
                MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY ||
                MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY;
        mMatchParentChildren.clear();

        int maxHeight = 0;
        int maxWidth = 0;
        int childState = 0;

        for (int i = 0; i < count; i++) {
            final View child = getChildAt(i);
            if (mMeasureAllChildren || child.getVisibility() != GONE) {
                measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
                maxWidth = Math.max(maxWidth,
                        child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
                maxHeight = Math.max(maxHeight,
                        child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
                childState = combineMeasuredStates(childState, child.getMeasuredState());
                if (measureMatchParentChildren) {
                    if (lp.width == LayoutParams.MATCH_PARENT ||
                            lp.height == LayoutParams.MATCH_PARENT) {
                        mMatchParentChildren.add(child);
                    }
                }
            }
        }

        // Account for padding too
        maxWidth += getPaddingLeftWithForeground() + getPaddingRightWithForeground();
        maxHeight += getPaddingTopWithForeground() + getPaddingBottomWithForeground();

        // Check against our minimum height and width
        maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
        maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());

        // Check against our foreground's minimum height and width
        final Drawable drawable = getForeground();
        if (drawable != null) {
            maxHeight = Math.max(maxHeight, drawable.getMinimumHeight());
            maxWidth = Math.max(maxWidth, drawable.getMinimumWidth());
        }

        setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
                resolveSizeAndState(maxHeight, heightMeasureSpec,
                        childState << MEASURED_HEIGHT_STATE_SHIFT));

        count = mMatchParentChildren.size();
        if (count > 1) {
            for (int i = 0; i < count; i++) {
                final View child = mMatchParentChildren.get(i);
                final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();

                final int childWidthMeasureSpec;
                if (lp.width == LayoutParams.MATCH_PARENT) {
                    final int width = Math.max(0, getMeasuredWidth()
                            - getPaddingLeftWithForeground() - getPaddingRightWithForeground()
                            - lp.leftMargin - lp.rightMargin);
                    childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
                            width, MeasureSpec.EXACTLY);
                } else {
                    childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
                            getPaddingLeftWithForeground() + getPaddingRightWithForeground() +
                            lp.leftMargin + lp.rightMargin,
                            lp.width);
                }

                final int childHeightMeasureSpec;
                if (lp.height == LayoutParams.MATCH_PARENT) {
                    final int height = Math.max(0, getMeasuredHeight()
                            - getPaddingTopWithForeground() - getPaddingBottomWithForeground()
                            - lp.topMargin - lp.bottomMargin);
                    childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
                            height, MeasureSpec.EXACTLY);
                } else {
                    childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec,
                            getPaddingTopWithForeground() + getPaddingBottomWithForeground() +
                            lp.topMargin + lp.bottomMargin,
                            lp.height);
                }

                child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
            }
        }
    }

我們可以看到祟敛,整個(gè)的onMeasure其實(shí)分為三步:

  • 遍歷所有子View疤坝,調(diào)用measureChildWithMargins進(jìn)行第一次子View的測(cè)量,在第一節(jié)中馆铁,我們也分析了這個(gè)方法跑揉,它最終也是調(diào)用子Viewmeasure方法。
  • 根據(jù)第一步的結(jié)果埠巨,調(diào)用setMeasuredDimension來(lái)設(shè)置自己的測(cè)量結(jié)果历谍。
  • 遍歷所有子View,根據(jù)第二步的結(jié)果辣垒,調(diào)用child.measure進(jìn)行第二次的測(cè)量望侈。

這也驗(yàn)證了第二節(jié)中的結(jié)論:父容器和子View的關(guān)聯(lián)是通過(guò)measure進(jìn)行關(guān)聯(lián)的
同時(shí)我們也可以有一個(gè)新的結(jié)論勋桶,對(duì)于View樹(shù)的某個(gè)節(jié)點(diǎn)脱衙,它的測(cè)量結(jié)果有可能并不是一次決定的,這是由于父容器可能需要依賴于子View的測(cè)量結(jié)果例驹,而父容器的結(jié)果又可能會(huì)影響子View捐韩,但是,我們需要保證這個(gè)過(guò)程不是無(wú)限調(diào)用的鹃锈。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末荤胁,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子仪召,更是在濱河造成了極大的恐慌寨蹋,老刑警劉巖松蒜,帶你破解...
    沈念sama閱讀 222,681評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異已旧,居然都是意外死亡秸苗,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門运褪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)惊楼,“玉大人,你說(shuō)我怎么就攤上這事秸讹√戳” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 169,421評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵璃诀,是天一觀的道長(zhǎng)弧可。 經(jīng)常有香客問(wèn)我,道長(zhǎng)劣欢,這世上最難降的妖魔是什么棕诵? 我笑而不...
    開(kāi)封第一講書人閱讀 60,114評(píng)論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮凿将,結(jié)果婚禮上校套,老公的妹妹穿的比我還像新娘。我一直安慰自己牧抵,他們只是感情好笛匙,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,116評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著犀变,像睡著了一般妹孙。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上弛作,一...
    開(kāi)封第一講書人閱讀 52,713評(píng)論 1 312
  • 那天涕蜂,我揣著相機(jī)與錄音,去河邊找鬼映琳。 笑死机隙,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的萨西。 我是一名探鬼主播有鹿,決...
    沈念sama閱讀 41,170評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼谎脯!你這毒婦竟也來(lái)了葱跋?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 40,116評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎娱俺,沒(méi)想到半個(gè)月后稍味,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,651評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡荠卷,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,714評(píng)論 3 342
  • 正文 我和宋清朗相戀三年模庐,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片油宜。...
    茶點(diǎn)故事閱讀 40,865評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡掂碱,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出慎冤,到底是詐尸還是另有隱情疼燥,我是刑警寧澤,帶...
    沈念sama閱讀 36,527評(píng)論 5 351
  • 正文 年R本政府宣布蚁堤,位于F島的核電站醉者,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏违寿。R本人自食惡果不足惜湃交,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,211評(píng)論 3 336
  • 文/蒙蒙 一熟空、第九天 我趴在偏房一處隱蔽的房頂上張望藤巢。 院中可真熱鬧,春花似錦息罗、人聲如沸掂咒。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 32,699評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)绍刮。三九已至,卻和暖如春挨摸,著一層夾襖步出監(jiān)牢的瞬間孩革,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,814評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工得运, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留膝蜈,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,299評(píng)論 3 379
  • 正文 我出身青樓熔掺,卻偏偏與公主長(zhǎng)得像饱搏,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子置逻,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,870評(píng)論 2 361

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