android事件攔截處理機制詳解

android事件處理芦昔,最復(fù)雜的就是對Touch事件的處理诱贿,因為Touch事件包括:down, move, up, cancle和多點觸摸等多種情況娃肿,多點觸摸的情況先不討論咕缎,因為Touch有這么多的狀態(tài),所以Touch相對來說是最難處理的料扰,下面就來討論一下android系統(tǒng)是如何處理Touch事件的

先來張參照圖:

Paste_Image.png

1.說到事件處理凭豪,首先我們要明白,為什么要處理事件晒杈,要了解android系統(tǒng)本身對事件的一個處理過程.在實際的開發(fā)中嫂伞,我們?nèi)绻加孟到y(tǒng)的基本控件,那是不需要去處理事件的拯钻,但是如果我們用復(fù)雜的布局嵌套去做一些特殊的需求帖努,例如:ScrollView中嵌套ListView,ScrollView嵌套ViewPager等,則會產(chǎn)生事件沖突粪般,所以拼余,由于事件沖突的存在,我們要去處理這些沖突亩歹,只有了解android的事件處理機制匙监,才能有效的去處理事件沖突.還有就是如果我們要新開發(fā)一個組件,則組件的所有事件都要我們自己去做處理小作,這種情況也需要我們?nèi)ヌ幚硎录裕河捎诖嬖谝陨险f到的兩種情況亭姥,我們要自己處理事件.
2.有了處理事件的動機后,接下來就要了解android系統(tǒng)本身是如何處理復(fù)雜的事件的.a(chǎn)ndroid系統(tǒng)為所有的事件提供了三個相關(guān)的方法顾稀,以下只以Touch事件為例說明.

這三個方法分別是:

dispatchTouchEvent(MotionEvent ev);  (Activity, ViewGroup, View都有此方法)

onInterceptTouchEvent(MotionEvent ev); (只有ViewGroup有)

onTouchEvent(MotionEvent);      (Activity, ViewGroup, View都有此方法) 

要想了解android系統(tǒng)是如何對事件進行一步一步的處理达罗,這三個方法是必須要掌握的.其中: dispatchTouchEvent(MotionEvent ev);方法是用來對事件進行分發(fā)的,即將事件分發(fā)到目標(biāo)控件静秆,onInterceptTouchEvent(MotionEvent ev)是用來過濾事件的粮揉,即進行事件的攔截,也就是是否要向下傳遞事件诡宗,onTouchEvent(MotionEvent ev)才是最終用來處理事件的滔蝉,也就是說我們平常重寫onTouchEvent時,其實塔沃,系統(tǒng)已經(jīng)默認幫我們調(diào)用了前兩個方法.下面就來詳細分析一下三個方法.
  首先要提的是蝠引,android系統(tǒng)對本件的處理是一層一層向下傳遞處理(樹形處理).那這棵樹是從那來的呢..就是我們的布局樹阳谍,一個布局,無論是代碼編寫的布局還是xml生成的布局螃概,android系統(tǒng)對它進行解析時都是將其組裝成一棵UI樹矫夯,最外層布局是整個UI樹的根.知道這個以后,再來分析事件的處理.
  處理流程:當(dāng)我們的手指觸摸到手機屏幕時吊洼,當(dāng)前處于onStart()狀態(tài)的Activity最先接收到此Touch事件下的ACTON_DOWN训貌,然后開始調(diào)用它自己dispatchTouchEvent()開始進行DOWN事件分發(fā),如果此方法返回true,則Activity不向下分發(fā)事件冒窍,則整個布局都不會收到DOWN事件递沪,TouchEvent直接到Activity的onTouchEvent()方法進行事件處理.如果返回false,則表示DOWN是要被分發(fā)到下層的,此時DOWN事件被直接分發(fā)(因為沒有過濾方法)到UI樹的根布局(即最外層的布局)综液,根布局拿到DOWN事件時款慨,執(zhí)行自己的dispatchTouchEvent方法,返回true,則事件直接交到根布局的onTouchEvent()中進行處理谬莹,false則表示還得向下分發(fā)檩奠,此時事件被傳遞到根布局的onInterceptTouchEvent()方法中,如果此方法返回true,表示要對此事件進行過濾附帽,則此DOWN事件又直接進行到根布局的onTouchEvent()方法直接處理埠戳,false則,要根布局不對事件進行過濾蕉扮,DOWN事件繼續(xù)向下傳遞整胃,直到達到目標(biāo)組件后,目標(biāo)組件調(diào)用自己的dispatchTouchEvent()方法慢显,由于是目標(biāo)組件爪模,直接分發(fā)事件到自己的onTouchEvent方法中,目標(biāo)組件如果處理完這個DOWN事件后返回true,表示該事件被消費完畢荚藻,不再向上層傳遞屋灌,如果返回false,則表示沒有消費完這個DOWN事件应狱,DOWN向上傳遞到自己的父組件中共郭,父組件再進行DOWN事件的處理.一直向上傳遞直到事件被扔到虛擬機.DOWN事件才算處理完成,接著調(diào)用MOVE,MOVE完了UP,整個流程與DOWN是一樣的.
這里要強調(diào)一點的是:如果一個組件沒有接收到DOWN事件疾呻,那么一定接收不到MOVE,UP事件除嘹。

通過以上的流程,我們可以明白:android系統(tǒng)對任何一個事件的處理都是這樣的岸蜗,分發(fā)事件尉咕,過濾事件,處理事件璃岳,下一個事件年缎, 分發(fā)事件悔捶,過濾事件,處理事件……一直這樣循環(huán)去處理所有的事件的单芜。即:事件的分發(fā)蜕该,過濾是從根到葉的,處理則是從葉再到根的

Paste_Image.png

從圖上看洲鸠,我們可以更直觀的感受整個Touch事件的處理流堂淡。

總結(jié):onInterceptTouchEvent負責(zé)對事件進行攔截,攔截成功后交給最先遇到onTouchEvent返回true的那個view進行處理扒腕。

看到這里如果你還是有點困惑绢淀,請想象一下生活中常見的場景:假如你所在的公司,有一個總經(jīng)理袜匿,級別最高更啄,它下面有個部長稚疹,級別次之居灯,最底層就是干活的你,沒有級別∧诠罚現(xiàn)在總經(jīng)理有一個任務(wù)怪嫌,總經(jīng)理將這個業(yè)務(wù)布置給部長,部長又把任務(wù)安排給你柳沙,當(dāng)你完成這個任務(wù)時岩灭,就把任務(wù)反饋給部長,部長覺得這個任務(wù)完成的不錯赂鲤,于是就簽了他的名字反饋給總經(jīng)理噪径,總經(jīng)理看了也覺得不錯,就也簽了名字交給董事會数初,這樣找爱,一個任務(wù)就順利完成了。這其實就是一個典型的事件攔截機制泡孩。

在這里我們先定義三個類:
一個總經(jīng)理—MyViewGroupA车摄,最外層的ViewGroup

一個部長—MyViewGroupB,中間的ViewGroup

一個你—MyView仑鸥,在最底層

根據(jù)以上的場景吮播,我們可以繪制以下流程圖:

Paste_Image.png

從圖中,我們可以看到在ViewGroup中眼俊,比View多了一個方法—onInterceptTouchEvent()方法意狠,這個是干嘛用的呢,是用來進行事件攔截的疮胖,如果被攔截环戈,事件就不會往下傳遞了誊役,不攔截則繼續(xù)。

如果我們稍微改動下谷市,如果總經(jīng)理(MyViewGroupA)發(fā)現(xiàn)這個任務(wù)太簡單蛔垢,覺得自己就可以完成,完全沒必要再找下屬迫悠,因此MyViewGroupA就使用了onInterceptTouchEvent()方法把事件給攔截了鹏漆,此時流程圖:

Paste_Image.png

我們可以看到,事件就傳遞到MyVewGroupA這里就不繼續(xù)傳遞下去了创泄,就直接返回艺玲。

如果我們再改動下,總經(jīng)理(MyViewGroupA)委托給部長(MyViewGroupB)鞠抑,部長覺得自己就可以完成饭聚,完全沒必要再找下屬,因此MyViewGroupB就使用了onInterceptTouchEvent()方法把事件給攔截了搁拙,此時流程圖:

Paste_Image.png

我們可以看到秒梳,MyViewGroupB攔截后,就不繼續(xù)傳遞了箕速,同理如果酪碘,到干貨的我們上(MyView),也直接返回True的話盐茎,事件也是不會繼續(xù)傳遞的兴垦,如圖:

Paste_Image.png

源碼

分析Android View事件傳遞機制之前有必要先看下源碼的一些關(guān)系,如下是幾個繼承關(guān)系圖:

Paste_Image.png
Paste_Image.png

看了官方這個繼承圖是不是明白了上面例子中說的LinearLayout是ViewGroup的子類字柠,ViewGroup是View的子類探越,Button是View的子類關(guān)系呢?其實窑业,在Android中所有的控件無非都是ViewGroup或者View的子類钦幔,說高尚點就是所有控件都是View的子類。
從View的dispatchTouchEvent方法說起

在Android中你只要觸摸控件首先都會觸發(fā)控件的dispatchTouchEvent方法(其實這個方法一般都沒在具體的控件類中数冬,而在他的父類View中)节槐,所以我們先來看下View的dispatchTouchEvent方法,如下:

 public boolean dispatchTouchEvent(MotionEvent event) {
        // If the event should be handled by accessibility focus first.
        if (event.isTargetAccessibilityFocus()) {
            // We don't have focus or no virtual descendant has it, do not handle the event.
            if (!isAccessibilityFocusedViewOrHost()) {
                return false;
            }
            // We have focus and got the event, then use normal event dispatch.
            event.setTargetAccessibilityFocus(false);
        }

        boolean result = false;

        if (mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onTouchEvent(event, 0);
        }

        final int actionMasked = event.getActionMasked();
        if (actionMasked == MotionEvent.ACTION_DOWN) {
            // Defensive cleanup for new gesture
            stopNestedScroll();
        }

        if (onFilterTouchEventForSecurity(event)) {
            //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;
            }
        }

        if (!result && mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
        }

        // Clean up after nested scrolls if this is the end of a gesture;
        // also cancel it if we tried an ACTION_DOWN but we didn't want the rest
        // of the gesture.
        if (actionMasked == MotionEvent.ACTION_UP ||
                actionMasked == MotionEvent.ACTION_CANCEL ||
                (actionMasked == MotionEvent.ACTION_DOWN && !result)) {
            stopNestedScroll();
        }

        return result;
    }```
dispatchTouchEvent的代碼有點長拐纱,但可以挑幾個重點講講铜异,if (onFilterTouchEventForSecurity(event))語句判斷當(dāng)前View是否沒被遮住等,然后定義ListenerInfo局部變量秸架,ListenerInfo是View的靜態(tài)內(nèi)部類揍庄,用來定義一堆關(guān)于View的XXXListener等方法;接著if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && li.mOnTouchListener.onTouch(this, event))語句就是重點东抹,首先li對象自然不會為null蚂子,li.mOnTouchListener呢沃测?你會發(fā)現(xiàn)ListenerInfo的mOnTouchListener成員是在哪兒賦值的呢?怎么確認他是不是null呢食茎?通過在View類里搜索可以看到:

/**
* Register a callback to be invoked when a touch event is sent to this view.
* @param l the touch listener to attach to this view
*/
public void setOnTouchListener(OnTouchListener l) {
getListenerInfo().mOnTouchListener = l;
}```
li.mOnTouchListener是不是null取決于控件(View)是否設(shè)置setOnTouchListener監(jiān)聽蒂破,在上面的實例中我們是設(shè)置過Button的setOnTouchListener方法的,所以也不為null别渔,接著通過位與運算確定控件(View)是不是ENABLED 的附迷,默認控件都是ENABLED 的,接著判斷onTouch的返回值是不是true哎媚。通過如上判斷之后如果都為true則設(shè)置默認為false的result為true喇伯,那么接下來的if (!result && onTouchEvent(event))就不會執(zhí)行,最終dispatchTouchEvent也會返回true拨与。而如果if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && li.mOnTouchListener.onTouch(this, event))語句有一個為false則if (!result && onTouchEvent(event))就會執(zhí)行稻据,如果onTouchEvent(event)返回false則dispatchTouchEvent返回false,否則返回true买喧。

這下再看前面的實例部分明白了吧捻悯?控件觸摸就會調(diào)運dispatchTouchEvent方法,而在dispatchTouchEvent中先執(zhí)行的是onTouch方法岗喉,所以驗證了實例結(jié)論總結(jié)中的onTouch優(yōu)先于onClick執(zhí)行道理秋度。如果控件是ENABLE且在onTouch方法里返回了true則dispatchTouchEvent方法也返回true,不會再繼續(xù)往下執(zhí)行钱床;反之,onTouch返回false則會繼續(xù)向下執(zhí)行onTouchEvent方法埠居,且dispatchTouchEvent的返回值與onTouchEvent返回值相同

dispatchTouchEvent總結(jié) :

在View的觸摸屏傳遞機制中通過分析dispatchTouchEvent方法源碼我們會得出如下基本結(jié)論:

觸摸控件(View)首先執(zhí)行dispatchTouchEvent方法查牌。
在dispatchTouchEvent方法中先執(zhí)行onTouch方法,后執(zhí)行onClick方法(onClick方法在onTouchEvent中執(zhí)行滥壕,下面會分析)纸颜。
如果控件(View)的onTouch返回false或者mOnTouchListener為null(控件沒有設(shè)置setOnTouchListener方法)或者控件不是enable的情況下會調(diào)運onTouchEvent,dispatchTouchEvent返回值與onTouchEvent返回一樣绎橘。
如果控件不是enable的設(shè)置了onTouch方法也不會執(zhí)行胁孙,只能通過重寫控件的onTouchEvent方法處理(上面已經(jīng)處理分析了),dispatchTouchEvent返回值與onTouchEvent返回一樣称鳞。
如果控件(View)是enable且onTouch返回true情況下涮较,dispatchTouchEvent直接返回true,不會調(diào)用onTouchEvent方法冈止。

onTouchEvent方法 :

public boolean onTouchEvent(MotionEvent event) {
        final float x = event.getX();
        final float y = event.getY();
        final int viewFlags = mViewFlags;

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

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

        if (((viewFlags & CLICKABLE) == CLICKABLE ||
                (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_UP:
                    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) {
                            // 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)) {
                                    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();
                    }
                    break;

                case MotionEvent.ACTION_DOWN:
                    mHasPerformedLongPress = false;

                    if (performButtonActionOnTouchDown(event)) {
                        break;
                    }

                    // 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
                        setPressed(true, x, y);
                        checkForLongClick(0);
                    }
                    break;

                case MotionEvent.ACTION_CANCEL:
                    setPressed(false);
                    removeTapCallback();
                    removeLongPressCallback();
                    break;

                case MotionEvent.ACTION_MOVE:
                    drawableHotspotChanged(x, y);

                    // Be lenient about moving outside of buttons
                    if (!pointInView(x, y, mTouchSlop)) {
                        // Outside button
                        removeTapCallback();
                        if ((mPrivateFlags & PFLAG_PRESSED) != 0) {
                            // Remove any future long press/tap checks
                            removeLongPressCallback();

                            setPressed(false);
                        }
                    }
                    break;
            }

            return true;
        }

        return false;
    }```
首先地6到14行可以看出狂票,如果控件(View)是disenable狀態(tài),同時是可以clickable的則onTouchEvent直接消費事件返回true熙暴,反之如果控件(View)是disenable狀態(tài)闺属,同時是disclickable的則onTouchEvent直接false慌盯。多說一句,關(guān)于控件的enable或者clickable屬性可以通過java或者xml直接設(shè)置掂器,每個view都有這些屬性亚皂。

接著22行可以看見,如果一個控件是enable且disclickable則onTouchEvent直接返回false了国瓮;反之孕讳,如果一個控件是enable且clickable則繼續(xù)進入過于一個event的switch判斷中,然后最終onTouchEvent都返回了true巍膘。switch的ACTION_DOWN與ACTION_MOVE都進行了一些必要的設(shè)置與置位厂财,接著到手抬起來ACTION_UP時你會發(fā)現(xiàn),首先判斷了是否按下過峡懈,同時是不是可以得到焦點璃饱,然后嘗試獲取焦點,然后判斷如果不是longPressed則通過post在UI Thread中執(zhí)行一個PerformClick的Runnable肪康,也就是performClick方法荚恶。具體如下:

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

這個方法也是先定義一個ListenerInfo的變量然后賦值,接著判斷l(xiāng)i.mOnClickListener是不是為null磷支,決定執(zhí)行不執(zhí)行onClick谒撼。你指定現(xiàn)在已經(jīng)很機智了,和onTouch一樣雾狈,搜一下mOnClickListener在哪賦值的唄廓潜,結(jié)果發(fā)現(xiàn):

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

控件只要監(jiān)聽了onClick方法則mOnClickListener就不為null,而且有意思的是如果調(diào)運setOnClickListener方法設(shè)置監(jiān)聽且控件是disclickable的情況下默認會幫設(shè)置為clickable善榛。

onTouchEvent小結(jié) :

onTouchEvent方法中會在ACTION_UP分支中觸發(fā)onClick的監(jiān)聽辩蛋。
當(dāng)dispatchTouchEvent在進行事件分發(fā)的時候,只有前一個action返回true移盆,才會觸發(fā)下一個action悼院。

通過以上總結(jié),Android中的事件攔截機制咒循,其實跟我們生活中的上下級委托任務(wù)很像据途,領(lǐng)導(dǎo)可以處理掉,也可以下發(fā)給下屬員工處理叙甸,如果員工處理的好颖医,領(lǐng)導(dǎo)才敢給你下發(fā)任務(wù),如果你處理不好蚁署,則領(lǐng)導(dǎo)也不敢把任務(wù)交給你便脊,這就像在中途把下發(fā)的任務(wù)的中途攔截掉了。通過流程和源碼的分析,相信大家能比較容易了解事件的分發(fā)哪痰、攔截遂赠、處理事件的流程。學(xué)習(xí)過程中晌杰,保持好奇心是很重要的跷睦。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市肋演,隨后出現(xiàn)的幾起案子抑诸,更是在濱河造成了極大的恐慌,老刑警劉巖爹殊,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蜕乡,死亡現(xiàn)場離奇詭異,居然都是意外死亡梗夸,警方通過查閱死者的電腦和手機层玲,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來反症,“玉大人辛块,你說我怎么就攤上這事∏Π” “怎么了润绵?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵,是天一觀的道長胞谈。 經(jīng)常有香客問我尘盼,道長,這世上最難降的妖魔是什么呜魄? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任悔叽,我火速辦了婚禮,結(jié)果婚禮上爵嗅,老公的妹妹穿的比我還像新娘。我一直安慰自己笨蚁,他們只是感情好睹晒,可當(dāng)我...
    茶點故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著括细,像睡著了一般伪很。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上奋单,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天锉试,我揣著相機與錄音,去河邊找鬼览濒。 笑死呆盖,一個胖子當(dāng)著我的面吹牛拖云,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播应又,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼宙项,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了株扛?” 一聲冷哼從身側(cè)響起尤筐,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎洞就,沒想到半個月后盆繁,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡旬蟋,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年油昂,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片咖为。...
    茶點故事閱讀 40,675評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡秕狰,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出躁染,到底是詐尸還是另有隱情鸣哀,我是刑警寧澤,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布吞彤,位于F島的核電站我衬,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏饰恕。R本人自食惡果不足惜挠羔,卻給世界環(huán)境...
    茶點故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望埋嵌。 院中可真熱鬧破加,春花似錦、人聲如沸雹嗦。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽了罪。三九已至锭环,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間泊藕,已是汗流浹背辅辩。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人玫锋。 一個月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓蛾茉,卻偏偏與公主長得像,于是被迫代替她去往敵國和親景醇。 傳聞我的和親對象是個殘疾皇子臀稚,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,685評論 2 360

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