Android基礎(chǔ)知識(shí)View篇:View的事件分發(fā)機(jī)制源碼解析

1.介紹

1.MotionEvent

當(dāng)我們點(diǎn)擊屏幕時(shí)界弧,就產(chǎn)生了點(diǎn)擊事件气嫁,這個(gè)點(diǎn)擊事件被封裝成了一個(gè)類(lèi):MotionEvent料睛。當(dāng)手機(jī)接觸屏幕后會(huì)產(chǎn)生一系列的事件府蔗,其中典型的事件類(lèi)型有如下幾種:

  • ACTION_DOWN:手指剛接觸屏幕灾票;
  • ACTION_MOVE:手指在屏幕上移動(dòng)峡谊;
  • ACTION_UP:手指從屏幕上松開(kāi)的一瞬間。

2.事件分發(fā)的3個(gè)重要方法

當(dāng)一個(gè)MotionEvent產(chǎn)生后刊苍,系統(tǒng)需要把這個(gè)事件傳遞給一個(gè)具體的View既们,這個(gè)傳遞過(guò)程就是事件分發(fā)過(guò)程。事件的分發(fā)的過(guò)程由3個(gè)重要的方法來(lái)共同完成:

  • public boolean dispatchTouchEvent(MotionEvent ev)
    用來(lái)進(jìn)行事件的分發(fā)正什。如果事件能夠傳遞給當(dāng)前View啥纸,那么此方法一定會(huì)被調(diào)用,返回結(jié)果受當(dāng)前View的TouchEvent和下級(jí)View的dispatchTouchEvent方法影響婴氮,表示是否消耗當(dāng)前事件斯棒。
  • public boolean onInterceptTouchEvent(MotionEvent ev)
    在dispatchTouchEvent方法中調(diào)用,用來(lái)判斷是否攔截某個(gè)事件主经。如果當(dāng)前View攔截了某個(gè)事件荣暮,那么在同一個(gè)事件序列當(dāng)中,此方法不會(huì)被再次調(diào)用罩驻,返回結(jié)果表示是否攔截當(dāng)前事件穗酥。需要注意的是View沒(méi)有提供該方法。
  • public boolean onTouchEvent(MotionEvent event)
    在dispatchTouchEvent方法中調(diào)用惠遏,用來(lái)處理點(diǎn)擊事件砾跃。返回結(jié)果表示是否消耗當(dāng)前事件,如果不消耗爽哎,則在同一個(gè)事件序列中蜓席,當(dāng)前View無(wú)法再次接收到事件。

2.Activity對(duì)事件的分發(fā)

當(dāng)一個(gè)事件產(chǎn)生時(shí)课锌,事件會(huì)最先傳遞給當(dāng)前Activity,即Activity的dispatchTouchEvent方法會(huì)被調(diào)用祈秕。在Activity的dispatchTouchEvent方法中會(huì)將事件交給PhoneWindow處理渺贤,PhoneWindow又會(huì)將事件傳遞給DecorView,即事件傳遞順序?yàn)椋?strong>Activity->Window->DecorView请毛。我們先來(lái)看下ActivitydispatchTouchEvent方法志鞍,源碼如下:

//Activity的dispatchTouchEvent方法
public boolean dispatchTouchEvent(MotionEvent ev) {
    if (ev.getAction() == MotionEvent.ACTION_DOWN) {
        onUserInteraction();
    }
    if (getWindow().superDispatchTouchEvent(ev)) {
        return true;
    }
    return onTouchEvent(ev);
}

上面Activity的dispatchTouchEvent方法中,先調(diào)用了window的superDispatchTouchEvent方法方仿,交給window來(lái)處理固棚。如果window的superDispatchTouchEvent返回true统翩,表示事件被處理,整個(gè)事件循環(huán)就結(jié)束了此洲;如果返回false厂汗,表示事件沒(méi)有被處理,那么Activity的onTouchEvent方法會(huì)被調(diào)用呜师,事件由Activity來(lái)處理娶桦。

接下來(lái)看Window的superDispatchTouchEvent方法。通過(guò)源碼可以發(fā)現(xiàn)Window是個(gè)抽象類(lèi)汁汗,它的superDispatchTouchEvent方法也是個(gè)抽象方法衷畦。所以我們需要看Window的唯一實(shí)現(xiàn)類(lèi)PhoneWindow的處理,源碼如下:

//PhoneWindow的superDispatchTouchEvent
public boolean superDispatchTouchEvent(MotionEvent event){
    return mDecor.superDispatchTouchEvent(event);
}

在PhoneWindow的superDispatchTouchEvent方法中知牌,我們可以知道事件傳遞給了DecorView祈争。再看下DecorView的superDispatchTouchEvent方法,源碼如下:

public boolean superDispatchTouchEvent(MotionEvent event) {
    return super.dispatchTouchEvent(event);
}

DecorView繼承自FrameLayout角寸,它的super.dispatchTouchEvent方法調(diào)用的就是ViewGroup的dispatchTouchEvent方法菩混。并且Activity中通過(guò)setContentView所設(shè)置的根View,是getWindow.getDecorView().findViewById(Android.R.id.content).getChildAt(0)返回的View袭厂,那么可以確定根View是DecorView的一個(gè)子元素墨吓。所以事件傳遞到DecorView之后,其實(shí)就是ViewGroup的事件分發(fā)過(guò)程纹磺。

3.ViewGrou對(duì)事件的分發(fā)

ViewGroup對(duì)事件的分發(fā)過(guò)程帖烘,主要實(shí)現(xiàn)是在它的dispatchTouchEvent方法中。這個(gè)方法大概邏輯是先判斷是否攔截事件橄杨,如果不攔截事件秘症,則將事件交給子元素處理,如果攔截了事件或者子元素沒(méi)有處理事件則由自己處理事件式矫。

1.判斷是否攔截

先來(lái)看下如何判斷是否攔截事件的乡摹,源碼如下:

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    //...省略無(wú)關(guān)代碼
    
    final boolean intercepted;
    //1. 判斷是否有子元素處理了該事件
    if (actionMasked == MotionEvent.ACTION_DOWN
            || mFirstTouchTarget != null) {
        final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
        //2. 判斷子元素是否設(shè)置了禁止攔截的標(biāo)記位
        if (!disallowIntercept) {
            //3. 調(diào)用自己的onInterceptTouchEvent方法,詢(xún)問(wèn)自己是否要攔截事件
            intercepted = onInterceptTouchEvent(ev);
            ev.setAction(action); // restore action in case it was changed
        } else {
            intercepted = false;
        }
    } else {
        intercepted = true;
    }  
    //...省略無(wú)關(guān)代碼
}

上面源碼可以看到采转,有兩個(gè)判斷聪廉,如果都為true,則會(huì)調(diào)用自己的onInterceptTouchEvent方法故慈,來(lái)詢(xún)問(wèn)自己是否攔截事件板熊。那么什么情況下不調(diào)用自己的onInterceptTouchEvent方法呢?

首先第一個(gè)判斷mFirstTouchTarget != null察绷,從后面的邏輯可以看出來(lái)干签,當(dāng)事件由子元素成功處理時(shí),mFirstTouchTarget會(huì)被賦值并指向子元素拆撼。

那么 (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) 這個(gè)判斷的主要目的就是容劳,當(dāng)沒(méi)有子元素去處理事件時(shí)喘沿,則該事件序列中的其它事件(move、up等事件)到來(lái)時(shí)竭贩,ViewGroup會(huì)默認(rèn)攔截事件蚜印,并且不再調(diào)用onInterceptTouchEvent方法。

第二個(gè)判斷boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0娶视,這里的FLAG_DISALLOW_INTERCEPT標(biāo)記位是通過(guò)requestDisallowInterceptTouchEvent方法設(shè)置的晒哄,一般用于子View中。一旦FLAG_DISALLOW_INTERCEPT設(shè)置后肪获,ViewGroup將無(wú)法攔截除了down事件以外的其它事件寝凌。為什么說(shuō)是down以外的其它事件呢?這是因?yàn)樾⒑眨琕iewGroup在分發(fā)事件時(shí)较木,如果是down事件機(jī)會(huì)重置這個(gè)標(biāo)記位。源碼如下:

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    //...省略無(wú)關(guān)代碼
    
    // Throw away all previous state when starting a new touch gesture.
    // The framework may have dropped the up or cancel event for the previous gesture
    // due to an app switch, ANR, or some other state change.
    if (actionMasked == MotionEvent.ACTION_DOWN) {
        cancelAndClearTouchTargets(ev);
        resetTouchState();
    }
}

3.具體分發(fā)過(guò)程

如果ViewGroup不攔截事件青柄,會(huì)將事件交由它的子元素處理伐债,這段源碼如下:

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    //...省略無(wú)關(guān)代碼
    //如果不攔截事件,遍歷所有子元素致开,判斷子元素是否能夠接收事件
    if (!canceled && !intercepted) {

        final View[] children = mChildren;
        for (int i = childrenCount - 1; i >= 0; i--) {//1. 倒序遍歷子元素
            final int childIndex = getAndVerifyPreorderedIndex(
                    childrenCount, i, customOrder);
            final View child = getAndVerifyPreorderedView(
                    preorderedList, children, childIndex);

            // If there is a view that has accessibility focus we want it
            // to get the event first and if not handled we will perform a
            // normal dispatch. We may do a double iteration but this is
            // safer given the timeframe.
            if (childWithAccessibilityFocus != null) {
                if (childWithAccessibilityFocus != child) {
                    continue;
                }
                childWithAccessibilityFocus = null;
                i = childrenCount - 1;
            }

            //2. 判斷子元素是否可以接收點(diǎn)擊事件
            if (!child.canReceivePointerEvents()
                    || !isTransformedTouchPointInView(x, y, child, null)) {
                ev.setTargetAccessibilityFocus(false);
                continue;
            }

            newTouchTarget = getTouchTarget(child);
            if (newTouchTarget != null) {
                // Child is already receiving touch within its bounds.
                // Give it the new pointer in addition to the ones it is handling.
                newTouchTarget.pointerIdBits |= idBitsToAssign;
                break;
            }

            resetCancelNextUpFlag(child);
            //3. 事件分發(fā)
            if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
                // Child wants to receive touch within its bounds.
                mLastTouchDownTime = ev.getDownTime();
                if (preorderedList != null) {
                    // childIndex points into presorted list, find original index
                    for (int j = 0; j < childrenCount; j++) {
                        if (children[childIndex] == mChildren[j]) {
                            mLastTouchDownIndex = j;
                            break;
                        }
                    }
                } else {
                    mLastTouchDownIndex = childIndex;
                }
                mLastTouchDownX = ev.getX();
                mLastTouchDownY = ev.getY();
                newTouchTarget = addTouchTarget(child, idBitsToAssign);
                alreadyDispatchedToNewTouchTarget = true;
                break;
            }
        }
    }
}

從上面源碼可以看到峰锁,首先ViewGroup會(huì)遍歷所有子元素,然后判斷子元素是否能夠接收點(diǎn)擊事件双戳。是否能夠接收點(diǎn)擊事件主要由兩點(diǎn)來(lái)衡量:子元素是否在播放動(dòng)畫(huà)和點(diǎn)擊事件的坐標(biāo)是否落在子元素的區(qū)域內(nèi)虹蒋。如果某個(gè)子元素滿(mǎn)足這兩個(gè)條件,就會(huì)調(diào)用dispatchTransformedTouchEvent方法將事件交給這個(gè)子元素處理飒货。

接下來(lái)我們看下View的dispatchTransformedTouchEvent方法魄衅,在該方法內(nèi)部有如下一段內(nèi)容:

private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
        View child, int desiredPointerIdBits) {
    final boolean handled;
    
    //...省略無(wú)關(guān)代碼
    if (child == null) {
        handled = super.dispatchTouchEvent(event);
    } else {
        //省略無(wú)關(guān)代碼
        handled = child.dispatchTouchEvent(event);
    }
    
    return handled;
}

因?yàn)樵谙蜃釉胤职l(fā)事件時(shí),child不為null塘辅,所以它會(huì)直接調(diào)用child的dispatchTouchEvent方法晃虫,將事件交給子元素處理。并且可以看到dispatchTransformedTouchEvent的返回值就是child.dispatchTouchEvent(event)的返回值扣墩。

再回到ViewGroup的dispatchTouchEvent方法中哲银,可以看如果dispatchTransformedTouchEvent返回true,那么mFirstTouchTarget就會(huì)被賦值呻惕,同時(shí)break(跳出循環(huán))盘榨。當(dāng)然,如果返回false蟆融,ViewGroup會(huì)繼續(xù)向下一個(gè)子元素分發(fā)。如下所示:

//事件分發(fā)
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
    //...省略無(wú)關(guān)代碼
    newTouchTarget = addTouchTarget(child, idBitsToAssign);
    alreadyDispatchedToNewTouchTarget = true;
    break;
}

mFirstTouchTarget的賦值是在過(guò)程是在addTouchTarget方法內(nèi)部完成的守呜,addTouchTarget方法內(nèi)部可以看出型酥,mFirstTouchTarget是一種單鏈表結(jié)構(gòu)山憨。前面提到過(guò)mFirstTouchTarget,mFirstTouchTarget是否賦值弥喉,將影響VIewGroup對(duì)事件的攔截策略郁竟。如果mFirstTouchTarget為null,那么ViewGroup就會(huì)默認(rèn)攔截同一序列中down事件以外的其它所有事件由境。mFirstTouchTarget賦值源碼如下:

private TouchTarget addTouchTarget(@NonNull View child, int pointerIdBits) {
    final TouchTarget target = TouchTarget.obtain(child, pointerIdBits);
    target.next = mFirstTouchTarget;
    mFirstTouchTarget = target;
    return target;
}

3.ViewGroup自己處理事件

如果ViewGroup攔截了事件或者它的所有子元素都沒(méi)有處理事件棚亩,mFirstTouchTarget為null,那么ViewGroup會(huì)自己處理事件虏杰,這段代碼如下:


@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    //...省略無(wú)關(guān)代碼
    
    if (mFirstTouchTarget == null) {
    // No touch targets so treat this as an ordinary view.
        handled = dispatchTransformedTouchEvent(ev, canceled, null,
                TouchTarget.ALL_POINTER_IDS);
    }
    //...省略無(wú)關(guān)代碼
}

在這里的dispatchTransformedTouchEvent方法第三個(gè)參數(shù)child為null讥蟆。前面有介紹這個(gè)方法,如果child為null纺阔,會(huì)調(diào)用super.dispatchTouchEvent方法瘸彤,而ViewGroup的父類(lèi)是View,所以實(shí)際調(diào)用的是View的dispatchTouchEvent方法笛钝。那么可以知道质况,無(wú)論事件是分發(fā)給子View還是ViewGroup自己處理,都會(huì)轉(zhuǎn)到View的dispatchTouchEvent方法玻靡,接下來(lái)繼續(xù)分析View的dispatchTouchEvent方法结榄。

4.View對(duì)事件的處理

1.View的dispatchTouchEvent方法

因?yàn)閂iew(這里不包含ViewGroup)是一個(gè)單獨(dú)的元素,它沒(méi)有子元素?zé)o法向下傳遞事件囤捻,因此它只能自己處理事件臼朗。源碼如下:

public boolean dispatchTouchEvent(MotionEvent event) {
    boolean result = false;
    //...省略無(wú)關(guān)代碼
    if (onFilterTouchEventForSecurity(event)) {
        if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
            result = true;
        }
        //noinspection SimplifiableIfStatement
        ListenerInfo li = mListenerInfo;
        if (li != null && li.mOnTouchListener != null
                && (mViewFlags & ENABLED_MASK) == ENABLED
                && li.mOnTouchListener.onTouch(this, event)) {
            result = true;
        }
    
        if (!result && onTouchEvent(event)) {
            result = true;
        }
    }
    
    //...省略無(wú)關(guān)代碼
    return result;
}

從上面的源碼可以看出View對(duì)點(diǎn)擊事件的處理過(guò)程,首先會(huì)判斷有沒(méi)有設(shè)置OnTouchListener最蕾。如果OnTouchListener中的onTouch方法返回true依溯,那么onTouchEvent就不會(huì)被調(diào)用,可見(jiàn)OnTouchListener的優(yōu)先級(jí)高于OnTouchEvent瘟则,這樣做的好處是方便在外界處理點(diǎn)擊事件黎炉。

2.View的OnTouchEvent方法

下面看下OnTouchEvent的實(shí)現(xiàn),首先當(dāng)View處于不可用狀態(tài)下照樣會(huì)消耗點(diǎn)擊事件醋拧,盡管它看起來(lái)不可用慷嗜。這段源碼如下:

    final int action = event.getAction();
    
    final boolean clickable = ((viewFlags & CLICKABLE) == CLICKABLE
            || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
            || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE;
    
    if ((viewFlags & ENABLED_MASK) == DISABLED
            && (mPrivateFlags4 & PFLAG4_ALLOW_CLICK_WHEN_DISABLED) == 0) {
        if (action == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {
            setPressed(false);
        }
        mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
        // A disabled view that is clickable still consumes the touch
        // events, it just doesn't respond to them.
        return clickable;
    }

接著,如果View設(shè)置有代理丹壕,那么還會(huì)執(zhí)行TouchDelegate的onTouchEvent方法庆械,這個(gè)onTouchEvent的工作機(jī)制看起來(lái)和OnTouchListener類(lèi)似。

    if (mTouchDelegate != null) {
        if (mTouchDelegate.onTouchEvent(event)) {
            return true;
        }
    }

接下來(lái)看下onTouchEvent中對(duì)點(diǎn)擊事件的具體處理菌赖,如下所示:

public boolean onTouchEvent(MotionEvent event) {
    final boolean clickable = ((viewFlags & CLICKABLE) == CLICKABLE
            || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
            || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE;

    if (clickable) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_UP:
                boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
                if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
                    //...省略無(wú)關(guān)代碼
                    if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
                        // This is a tap, so remove the longpress check
                        removeLongPressCallback();
                        // Only perform take click actions if we were in the pressed state
                        if (!focusTaken) {
                            // Use a Runnable and post this rather than calling
                            // performClick directly. This lets other visual state
                            // of the view update before click actions start.
                            if (mPerformClick == null) {
                                mPerformClick = new PerformClick();
                            }
                            if (!post(mPerformClick)) {
                                performClickInternal();
                            }
                        }
                    }
                    //...省略無(wú)關(guān)代碼
                }
                break;
            case MotionEvent.ACTION_DOWN:
                //...省略無(wú)關(guān)代碼
                break;
            case MotionEvent.ACTION_CANCEL:
                //...省略無(wú)關(guān)代碼
                break;
            case MotionEvent.ACTION_MOVE:
                //...省略無(wú)關(guān)代碼
                break;
        }
        return true;
    }

    return false;
}

從上面的代碼來(lái)看瓣俯,只要View的CLICKABLE和LONG_CLICKABLE有一個(gè)為true,那么它就會(huì)消耗這個(gè)事件蛋叼,即onTouchEvent方法返回true,不管它是不是DISABLE狀態(tài)策幼。然后就是當(dāng)ACTION_UP事件發(fā)生時(shí),會(huì)觸發(fā)performClick方法奴紧,如果View設(shè)置了onClickListener特姐,那么performClick方法內(nèi)部會(huì)調(diào)用它的onClick方法,如下所示:

public boolean performClick() {
    final boolean result;
    final ListenerInfo li = mListenerInfo;
    if (li != null && li.mOnClickListener != null) {
        playSoundEffect(SoundEffectConstants.CLICK);
        li.mOnClickListener.onClick(this);
        result = true;
    } else {
        result = false;
    }
    sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
    return result;
}

View的LONG_CLICKABLE屬性默認(rèn)為false黍氮,而CLICKABLE屬性是否為false和具體的View有關(guān)唐含。確切的來(lái)說(shuō)是可點(diǎn)擊的View其CLICKABLE為true,不可點(diǎn)擊的View其CLICKABLE為false沫浆,比如Button是可點(diǎn)擊的捷枯,TextView是不可點(diǎn)擊的。通過(guò)setClickable和setLongClickable可以分別改變View的CLICKABLE和LONG_CLICKABLE屬性件缸。另外铜靶,setOnClickListener會(huì)自動(dòng)將View的CLICKABLE設(shè)為true,setLongClickListener則會(huì)自動(dòng)將View的LONG_CLICKABLE設(shè)為true他炊,這一點(diǎn)從源碼中可以看出來(lái)争剿,如下所示:

public void setOnClickListener(@Nullable OnClickListener l) {
    if (!isClickable()) {
        setClickable(true);
    }
    getListenerInfo().mOnClickListener = l;
}

public void setOnLongClickListener(@Nullable OnLongClickListener l) {
    if (!isLongClickable()) {
        setLongClickable(true);
    }
    getListenerInfo().mOnLongClickListener = l;
}

到這里,事件分發(fā)機(jī)制的源碼也就分析完了痊末。

?著作權(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)容