Android View 事件分發(fā)機制

參考資料

鴻洋版事件分發(fā)機制
郭霖版事件分發(fā)機制
Android開發(fā)藝術探索

Android事件傳遞整體流程簡介

Android輸入事件的源頭是位于/dev/input/下的設備節(jié)點奕翔,而輸入事件的終點是由WMS管理的某個窗口铡俐,最終由窗口中的View處理。最初的輸入事件為內(nèi)核生成的原始事件嘹叫,而最終交付給窗口的則是KeyEvent(鍵盤)或MotionEvent(鼠標和觸摸屏)對象。輸入事件由Native層進入到Java層的第一個函數(shù)是InputEventReceiver.dispatchInputEvent()坦报,這樣我們的手指觸摸事件(MotionEvent)就傳遞到Java層馏鹤,到應用層的傳遞過程遵循如下順序:Activity->Window->View;即View事件最先傳遞給Activity,然后由Activity傳遞給Window瑟捣,最后由Window傳遞給View;頂級View(DecorView)接收到事件后栅干,就會按照事件分發(fā)機制去分發(fā)事件迈套;事件到達Java層以后是誰將事件傳遞給Activity?

//事件傳遞整體流程:底層生成原始事件后非驮,經(jīng)過一列加工處理之后交汤,將事件封裝成MotionEvent雏赦、keyEvent劫笙,然后傳遞到Java層的InputEventReceiver.dispatchTouchEvent中,事件到達Java層之后星岗,如何一步步傳遞到Activity填大?以下調用堆棧可以看到事件如何用從InputEventReceive傳遞到Activity俏橘;事件傳遞到Activity之后允华,由Activity傳遞給Window(PhoneWindow),最后由Window傳遞給頂級View寥掐;頂級View(DecorView)接收到事件后靴寂,就會按照事件分發(fā)機制去分發(fā)事件;本文主要是從Activity開始來分析事件分發(fā)機制召耘;

//事件詳細信息百炬,action表示事件類型,x,y表示事件發(fā)生的位置污它,deviceId是硬件設備的id值
EventTest: MainActivity dispatchTouchEvent: MotionEvent { action=ACTION_DOWN, actionButton=0, id[0]=0, x[0]=408.94913, y[0]=416.38184, toolType[0]=TOOL_TYPE_MOUSE, buttonState=BUTTON_PRIMARY, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=498914, downTime=498914, deviceId=13, source=0x2002 }
System.err: java.lang.Exception: EventTest2
System.err:     at .*********************.MainActivity.dispatchTouchEvent(MainActivity.java:68)
System.err:     at android.support.v7.view.WindowCallbackWrapper.dispatchTouchEvent(WindowCallbackWrapper.java:71)
System.err:     at com.android.internal.policy.DecorView.dispatchTouchEvent(DecorView.java:434)
System.err:     at android.view.View.dispatchPointerEvent(View.java:12029)
System.err:     at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:4834)
//在onProcess方法中剖踊,進行事件類型的判斷庶弃,然后根據(jù)不同的事件類型調用不同的處理方法
System.err:     at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:4644)
//在deliver中完成事件處理之后,調用finishInputEvent給輸入系統(tǒng)一個反饋德澈;
System.err:     at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4176)  
System.err:     at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:4229)
System.err:     at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:4195)
System.err:     at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:4322)
System.err:     at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:4203)
System.err:     at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:4379)
System.err:     at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4176)
System.err:     at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:4229)
System.err:     at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:4195)
System.err:     at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:4203)
System.err:     at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4176)
System.err:     at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:6707)
System.err:     at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:6681)
System.err:     at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:6642)
System.err:     at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:6810)
//事件由native層正式進入到Java層
System.err:     at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:187)
System.err:     at android.os.MessageQueue.nativePollOnce(Native Method)
System.err:     at android.os.MessageQueue.next(MessageQueue.java:325)
System.err:     at android.os.Looper.loop(Looper.java:142)
System.err:     at android.app.ActivityThread.main(ActivityThread.java:6627)
System.err:     at java.lang.reflect.Method.invoke(Native Method)
System.err:     at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
System.err:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)

ViewPostImeInputStage.onProcess():

    final class ViewPostImeInputStage extends InputStage {
        @Override
        protected int onProcess(QueuedInputEvent q) {
            // 當onProcess被回調時歇攻,processKeyEvent、processPointerEvent梆造、processTrackballEvent缴守、
            // processGenericMotionEvent至少有一個方法就會被調用,這些方法都是屬于ViewPostImeInputStage的镇辉。
            // 這些方法中都有一句很關鍵的一句代碼處理按鍵事件
            // mView.dispatchKeyEvent(event)
            // mView.dispatchPointerEvent(event)
            // mView.dispatchTrackballEvent(event)
            // mView.dispatchGenericMotionEvent(event)
            // mView的實例化在ViewRootImpl的setView方法中斧散,其實就是DecorView
            // 這樣一來,可以知道ViewPostImeInputStage將事件分發(fā)到了DecorView
            if (q.mEvent instanceof KeyEvent) {
                // Key事件會調用到processKeyEvent摊聋,處理key事件鸡捐,如鍵盤事件
                return processKeyEvent(q);
            } else {
                final int source = q.mEvent.getSource();
                if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
                    //觸摸屏事件(MotionEvent),鼠標事件(MotionEvent)麻裁,鼠標事件到應用層是MotionEvent,不是KeyEvent
                    return processPointerEvent(q);
                } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
                    //軌跡球事件
                    return processTrackballEvent(q);
                } else {
                    //其它motion事件箍镜,如游戲手柄
                    return processGenericMotionEvent(q);
                }
            }
        }
    }

事件分發(fā)整體流程:底層生成原始事件后,經(jīng)過一列加工處理之后煎源,將事件封裝成MotionEvent色迂、keyEvent,然后傳遞到Java層InputEventReceiver.dispatchTouchEvent中手销,事件到達Java層之后歇僧,如何一步步傳遞到Activity?通過調用堆椃嫱希可以看到事件如何用從InputEventReceive傳遞到Activity诈悍;事件傳遞到Activity之后,由Activity傳遞給Window(PhoneWindow)兽埃,最后由Window傳遞給頂級View侥钳;頂級View(DecorView)接收到事件后,就會按照事件分發(fā)機制去分發(fā)事件柄错;本文主要是從Activity開始來分析事件分發(fā)機制舷夺;
與事件分發(fā)過程相關的幾個方法主要有:
(1)dispatchTouchEvent:用來進行事件分發(fā),如果事件能夠傳遞給當前View售貌,那么次方法一定會被調用给猾,返回值受當前View的onTouchEvent和下級View的dispatchTouchEvent方法影響;
(2)onInterceptTouchEvent:在dispatchTouchEvent方法內(nèi)部調用颂跨,用來判斷是否攔截某個事件敢伸,如果當前VIew攔截了某個事件,那么在同一事件序列中毫捣,此方法不會再被調用详拙,返回結果表示是否攔截當前事件帝际;
(3)onTouch:在dispatchTouchEvent方法中調用,其調用優(yōu)先級高于onTouchEvent饶辙;
(4)onTouchEvent:在dispatchTouchEvent方法中調用蹲诀,用來處理點擊事件,反饋結果表示是否消耗當前事件弃揽,如果不消耗脯爪,則在同一事件序列中,當前View無法再次接受事件矿微;
(5)onclick:在發(fā)生點擊事件時痕慢,會調用該方法,表示有點擊事件涌矢;onclick發(fā)生的前提是當前View可以點擊掖举,并且它收到down和up事件;
(6)onLongClick:當長按事件發(fā)生時會回調該方法娜庇,手指或鼠標按下500ms之后就會發(fā)生長按事件塔次,如果onLongClick返回true,就 不會在調用onClick方法名秀,返回false励负,就會調用onClick;

1.Activity對事件的分發(fā)過程

Activity.dispatchTouchEvent代碼如下:

    /**
     * 一個點擊事件產(chǎn)生后匕得,它的傳遞過程遵循如下順序:
     * <h3>Activity——>Window——>View</h3>
     * 即事件總是先傳遞給Activity继榆,由Activity的dispatchTouchEvent方法來進行事件派發(fā),
     * 具體的工作是由Activity內(nèi)部的Window來完成的汁掠。Activity傳遞給Window后略吨,Window再
     * 傳遞給頂級View。頂級View接受事件后调塌,就會按照事件分發(fā)機制去分發(fā)事件晋南。
     * @param ev The touch screen event.
     *
     * @return boolean Return true if this event was consumed.
     */
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            onUserInteraction();
        }
        // 傳遞給Window對象。getWindow()返回的是PhoneWindow
        if (getWindow().superDispatchTouchEvent(ev)) {
            return true;
        }
        return onTouchEvent(ev);
    }

2.Window(PhoneWindow)對事件的分發(fā)過程:
    // This is the top-level view of the window, containing the window decor.
    //DecorView是頂級View,也叫根View
    private DecorView mDecor;
      @Override
    public boolean superDispatchTouchEvent(MotionEvent event) {
        // PhoneWindow將事件直接傳遞給了DecorView
        return mDecor.superDispatchTouchEvent(event);
    }

通過getWindow().getDecorView().findViewById(android.R.id.content).getChildAt(0)這種方法可以獲取Activity所設置的View羔砾,setContentView所設置的View是DecorView的子View,關于DecorView后續(xù)會專門進行講解偶妖,目前事件傳遞已經(jīng)傳遞到DecorView這里姜凄,及事件已經(jīng)從Activity傳遞到View中。

3.View事件分發(fā)機制

當事件傳遞到View中時趾访,首先進入View的dispatchTouchEvent

    /**
     * Pass the touch screen motion event down to the target view, or this
     * view if it is the target.
     *
     * 如果事件能夠傳遞給當前View态秧,那么此方法一定會被調用,
     * 返回結果受當前View的onTouchEvent和下級View的dispatchTouchEvent方法的影響扼鞋,表示是否消耗當前事件申鱼。
     * View是一個單獨的元素愤诱,它沒有子元素因此無法向下傳遞事件,
     * 所以它只能自己處理事件捐友,所以View的onTouchEvent方法默認返回true淫半。
     *
     * @param event The motion event to be dispatched.
     * @return True if the event was handled by the view, false otherwise.
     */
    public boolean dispatchTouchEvent(MotionEvent event) {
        if (onFilterTouchEventForSecurity(event)) {
            if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
                result = true;
            }
            //noinspection SimplifiableIfStatement
            ListenerInfo li = mListenerInfo;
            // 首先會判斷當前View有沒有設置OnTouchListener,
            // 如果OnTouchListener中的onTouch方法返回true匣砖,那么onTouchEvent方法就不會被調用科吭,
            // 可見OnTouchListener的優(yōu)先級要高于onTouchEvent,這樣做的好處是方便在外界處理點擊事件猴鲫。
            if (li != null && li.mOnTouchListener != null
                    && (mViewFlags & ENABLED_MASK) == ENABLED
                    && li.mOnTouchListener.onTouch(this, event)) {
                result = true;
            }

            if (!result && onTouchEvent(event)) {
                result = true;
            }
        }
        return result;
    }

4.View的onTouchEvent

接下來是View的onTouchEvent:

    public boolean onTouchEvent(MotionEvent event) {
    
        // 對點擊事件的具體處理对人。只要CLICKABLE和LONG_CLICKABLE有一個為true,那么它就會消耗這個事件拂共,因為返回了true
        // View的LONG_CLICKABLE屬性默認為false牺弄,而CLICKABLE屬性默認為true,不過具體的View的CLICKABLE又不一定宜狐,
        // 確切來說是可點擊的View其CLICKABLE屬性true猖闪,比如Button,不可點擊的View的CLICKABLE為false肌厨,比如TextView培慌。
        // 通過setClickable和setLongClickable可以設置這兩個屬性。
        // 另外setOnClickListener和setOnLongClickListener會自動將View的這兩個屬性設為true柑爸。
        if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {
            switch (action) {
                case MotionEvent.ACTION_UP:
                    mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
                    if ((viewFlags & TOOLTIP) == TOOLTIP) {
                        handleTooltipUp();
                    }
                    if (!clickable) {
                        removeTapCallback();
                        //移除長按監(jiān)測
                        removeLongPressCallback();
                        mInContextButtonPress = false;
                        mHasPerformedLongPress = false;
                        mIgnoreNextUpEvent = false;
                        break;
                    }
                    boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
                    if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
                        // take focus if we don't have it already and we should in
                        // touch mode.
                        boolean focusTaken = false;
                        if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
                            focusTaken = requestFocus();
                        }

                        if (prepressed) {
                            // The button is being released before we actually
                            // showed it as pressed.  Make it show the pressed
                            // state now (before scheduling the click) to ensure
                            // the user sees it.
                            setPressed(true, x, y);
                        }

                        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.
                                // 當up事件發(fā)生時吵护,會觸發(fā)performClick方法
                                if (mPerformClick == null) {
                                    mPerformClick = new PerformClick();
                                }
                                if (!post(mPerformClick)) {
                                    // 如果View設置OnClickListener,那么performClick就會調用View的onClick方法表鳍。
                                    performClick();
                                }
                            }
                        }

                        if (mUnsetPressedState == null) {
                            mUnsetPressedState = new UnsetPressedState();
                        }

                        if (prepressed) {
                            postDelayed(mUnsetPressedState,
                                    ViewConfiguration.getPressedStateDuration());
                        } else if (!post(mUnsetPressedState)) {
                            // If the post failed, unpress right now
                            mUnsetPressedState.run();
                        }

                        removeTapCallback();
                    }
                    mIgnoreNextUpEvent = false;
                    break;

                case MotionEvent.ACTION_DOWN:
                    // Walk up the hierarchy to determine if we're inside a scrolling container.
                    boolean isInScrollingContainer = isInScrollingContainer();

                    // For views inside a scrolling container, delay the pressed feedback for
                    // a short period in case this is a scroll.
                    if (isInScrollingContainer) {
                        mPrivateFlags |= PFLAG_PREPRESSED;
                        if (mPendingCheckForTap == null) {
                            mPendingCheckForTap = new CheckForTap();
                        }
                        mPendingCheckForTap.x = event.getX();
                        mPendingCheckForTap.y = event.getY();
                        postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
                    } else {
                        // Not inside a scrolling container, so show the feedback right away
                        //設置當前View的狀態(tài)馅而,以及更新View的drawable state,例如button按下譬圣、懸浮時顯示不同的背景瓮恭,即刷新背景
                        setPressed(true, x, y);
                        //開始長按監(jiān)測,所以View的長按監(jiān)測位于
                        checkForLongClick(0, x, y);
                    }
                    break;

                case MotionEvent.ACTION_CANCEL:
                    if (clickable) {
                        setPressed(false);
                    }
                    removeTapCallback();
                    removeLongPressCallback();
                    mInContextButtonPress = false;
                    mHasPerformedLongPress = false;
                    mIgnoreNextUpEvent = false;
                    mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
                    break;

                case MotionEvent.ACTION_MOVE:
                    // Be lenient about moving outside of buttons
                    if (!pointInView(x, y, mTouchSlop)) {
                        // Outside button
                        // Remove any future long press/tap checks
                        removeTapCallback();
                        removeLongPressCallback();//當移出當前View時厘熟,會刪除當前View的長按監(jiān)測屯蹦;
                        ...
                    }
                    break;
            }
            return true;
        }
        return false;
    }

    private void checkForLongClick(int delayOffset, float x, float y) {
        if ((mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE || (mViewFlags & TOOLTIP) == TOOLTIP) {
            mHasPerformedLongPress = false;

            if (mPendingCheckForLongPress == null) {
                mPendingCheckForLongPress = new CheckForLongPress();
            }
            mPendingCheckForLongPress.setAnchor(x, y);
            mPendingCheckForLongPress.rememberWindowAttachCount();
            mPendingCheckForLongPress.rememberPressedState();
            //ViewConfiguration.getLongPressTimeout()時間為500ms,所以當用戶按下去500ms之后就會回OnLongClickListener.onLongClick方法绳姨;
            //如果500ms之內(nèi)手指彈起登澜,會發(fā)生ACTION_UP事件,此時會移出長按監(jiān)測飘庄,這樣就不會發(fā)生長按事件
            postDelayed(mPendingCheckForLongPress,
                    ViewConfiguration.getLongPressTimeout() - delayOffset);
        }
    }
    public boolean performClick() {
        if (li != null && li.mOnClickListener != null) {
            playSoundEffect(SoundEffectConstants.CLICK);
            // 如果設置了OnClickListener監(jiān)聽器脑蠕,就回調onClick方法。
            li.mOnClickListener.onClick(this);
            result = true;
        } else {
            result = false;
        }
        return result;
    }

通過以上代碼可以看出:
(1)當發(fā)生ACTION_DOWN的時候,開始長按監(jiān)測谴仙,如果用戶手指按下時間超過500ms才會發(fā)生長按事件迂求,少于500ms就是onClick事件;可以得知晃跺,是否是長按事件是在Java層進行判斷的揩局,并且和ACTION_MOVE事件沒有關系。
(2)onLongClick方法返回true之后會將mHasPerformedLongPress屬性置為true哼审,在Up的時候就不會執(zhí)行onClick谐腰,如果onlongClick返回false在Up事件到來時就會執(zhí)行onClick
(3)onClick是否會發(fā)生的前提是當前View是可點擊的,并且它收到了Down和Up事件涩盾,
(4)只要View的CLICKABLE和LONG_CLICKABLE有一個為TRUE十气,那么該View就會消耗掉該事件,可以看出以上無論是Down春霍、Move還是Up事件砸西,最終都return true;

5.總結:

(1)整個View的事件分發(fā)流程:
View.dispatchEvent->View.setOnTouchListener->View.onTouchEvent
在dispatchTouchEvent中會進行OnTouchListener的判斷址儒,如果OnTouchListener不為null且返回true芹枷,則表示事件被消費,onTouchEvent不會被執(zhí)行莲趣;否則執(zhí)行onTouchEvent鸳慈。
(2)onTouchEvent中的DOWN,MOVE,UP
DOWN時:
a、首先設置標志為PREPRESSED喧伞,設置mHasPerformedLongPress=false ;然后發(fā)出一個115ms后的mPendingCheckForTap走芋;
b、如果115ms內(nèi)沒有觸發(fā)UP潘鲫,則將標志置為PRESSED翁逞,清除PREPRESSED標志,同時發(fā)出一個延時為500-115ms的溉仑,檢測長按任務消息挖函;
c、如果500ms內(nèi)(從DOWN觸發(fā)開始算)浊竟,則會觸發(fā)LongClickListener:
此時如果LongClickListener不為null怨喘,則會執(zhí)行回調,同時如果LongClickListener.onClick返回true逐沙,才把mHasPerformedLongPress設置為true;否則mHasPerformedLongPress依然為false;
MOVE時:
主要就是檢測用戶是否劃出控件哲思,如果劃出了:
115ms內(nèi),直接移除mPendingCheckForTap吩案;
115ms后,則將標志中的PRESSED去除帝簇,同時移除長按的檢查:removeLongPressCallback();
UP時:
a徘郭、如果115ms內(nèi)靠益,觸發(fā)UP,此時標志為PREPRESSED残揉,則執(zhí)行UnsetPressedState胧后,setPressed(false);會把setPress轉發(fā)下去,可以在View中復寫dispatchSetPressed方法接收抱环;
b壳快、如果是115ms-500ms間,即長按還未發(fā)生镇草,則首先移除長按檢測眶痰,執(zhí)行onClick回調;
c梯啤、如果是500ms以后竖伯,那么有兩種情況:
i.設置了onLongClickListener,且onLongClickListener.onClick返回true因宇,則點擊事件OnClick事件無法觸發(fā)七婴;
ii.沒有設置onLongClickListener或者onLongClickListener.onClick返回false,則點擊事件OnClick事件依然可以觸發(fā)察滑;
d打厘、最后執(zhí)行setPressed刷新背景,然后將PRESSED標識去除贺辰;

本篇博文完成了對View的事件分發(fā)機制的整個流程的說明户盯,并且對源碼進行了分析;下篇會講解ViewGroup的事件分發(fā)機制魂爪;

本文部分內(nèi)容直接從其他文章直接Copy而來先舷,感謝本文內(nèi)容所參考文章的作者;

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末滓侍,一起剝皮案震驚了整個濱河市蒋川,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌撩笆,老刑警劉巖捺球,帶你破解...
    沈念sama閱讀 218,451評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異夕冲,居然都是意外死亡氮兵,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,172評論 3 394
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來泛啸,“玉大人册烈,你說我怎么就攤上這事强经∧掀” “怎么了掺涛?”我有些...
    開封第一講書人閱讀 164,782評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長疼进。 經(jīng)常有香客問我薪缆,道長,這世上最難降的妖魔是什么伞广? 我笑而不...
    開封第一講書人閱讀 58,709評論 1 294
  • 正文 為了忘掉前任拣帽,我火速辦了婚禮,結果婚禮上嚼锄,老公的妹妹穿的比我還像新娘减拭。我一直安慰自己,他們只是感情好灾票,可當我...
    茶點故事閱讀 67,733評論 6 392
  • 文/花漫 我一把揭開白布峡谊。 她就那樣靜靜地躺著,像睡著了一般刊苍。 火紅的嫁衣襯著肌膚如雪既们。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,578評論 1 305
  • 那天正什,我揣著相機與錄音啥纸,去河邊找鬼。 笑死婴氮,一個胖子當著我的面吹牛斯棒,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播主经,決...
    沈念sama閱讀 40,320評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼荣暮,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了罩驻?” 一聲冷哼從身側響起穗酥,我...
    開封第一講書人閱讀 39,241評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎惠遏,沒想到半個月后砾跃,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,686評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡节吮,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,878評論 3 336
  • 正文 我和宋清朗相戀三年抽高,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片透绩。...
    茶點故事閱讀 39,992評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡翘骂,死狀恐怖壁熄,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情雏胃,我是刑警寧澤请毛,帶...
    沈念sama閱讀 35,715評論 5 346
  • 正文 年R本政府宣布志鞍,位于F島的核電站瞭亮,受9級特大地震影響,放射性物質發(fā)生泄漏固棚。R本人自食惡果不足惜统翩,卻給世界環(huán)境...
    茶點故事閱讀 41,336評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望此洲。 院中可真熱鬧厂汗,春花似錦、人聲如沸呜师。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,912評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽汁汗。三九已至衷畦,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間知牌,已是汗流浹背祈争。 一陣腳步聲響...
    開封第一講書人閱讀 33,040評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留角寸,地道東北人菩混。 一個月前我還...
    沈念sama閱讀 48,173評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像扁藕,于是被迫代替她去往敵國和親沮峡。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,947評論 2 355

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