Android事件分發(fā)

事件分發(fā)

Android可以通過(guò)觸摸屏幕來(lái)實(shí)現(xiàn)與用戶的交互赖歌,Android屏幕上顯示了各種各樣的view,這些view有可能是豎直排列也有可能是疊在一起的呵恢,那么我們觸摸屏幕的時(shí)候到底由哪個(gè)view來(lái)響應(yīng)他嚷,這個(gè)就是我們的view的事件分發(fā)

View的分類

  1. View,View只具有處理事件的能力
  • dispatchTouchEvent
  • onTouchEvent
  1. ViewGroup拍霜,ViewGroup具有分發(fā)事件和處理事件的能力(由于ViewGroup繼承自View)
  • dispatchTouchEvent
  • onInterceptTouchEvent
  • onTouchEvent
  • requestDisallowInterceptTouchEvent

事件的種類

對(duì)于單點(diǎn)觸摸來(lái)說(shuō)事件主要有四種

  1. Down,用戶手指按下的時(shí)候觸發(fā)
  2. Move薪介,用戶手指在屏幕上移動(dòng)的時(shí)候觸發(fā)
  3. Up沉御,用戶手指抬起的時(shí)候觸發(fā)
  4. Cancel,事件被取消的時(shí)候觸發(fā)(事件攔截的時(shí)候會(huì)觸發(fā)Cancel昭灵,點(diǎn)擊的時(shí)候按住屏幕滑動(dòng)等)

事件處理的流程

事件的處理一般都是在View中的dispatchTouchEvent進(jìn)行的吠裆,下面是其中比較重要的一段代碼

public boolean dispatchTouchEvent(MotionEvent event) {
    //...上面的方法
   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;
      }
      //.....下面的方法
    }
    return result;
}
  1. li != null && li.mOnTouchListener != null
    && (mViewFlags & ENABLED_MASK) == ENABLED
    && li.mOnTouchListener.onTouch(this, event)
    當(dāng)我們?cè)O(shè)置了setOnTouchListener的時(shí)候?qū)i進(jìn)行了初始化,并且li.mOnTouchListener也不為null烂完,那么必定會(huì)執(zhí)行onTouch方法试疙,如果onTouch方法返回true,那么result就為true抠蚣。
  2. if (!result && onTouchEvent(event)) {
    result = true;
    }
    當(dāng)result返回true的時(shí)候則不會(huì)執(zhí)行onTouchEvent方法祝旷。
    當(dāng)result返回false(意味著onTouch方法返回了false),那么就會(huì)去執(zhí)行onTouchEvent嘶窄,最終result也返回了true怀跛,dispatchTouchEvent也返回了true,也就是事件被消費(fèi)了柄冲。
    3.接下來(lái)就看onTouchEvent做了哪些操作
    public boolean onTouchEvent(MotionEvent event) {
        final float x = event.getX();
        final float y = event.getY();
        final int viewFlags = mViewFlags;
        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) {
            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;
        }
        if (mTouchDelegate != null) {
            if (mTouchDelegate.onTouchEvent(event)) {
                return true;
            }
        }

        if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {
            switch (action) {
                case MotionEvent.ACTION_UP:
                       //長(zhǎng)按事件是由handler去處理的吻谋,如果不是長(zhǎng)按事件那么就會(huì)執(zhí)行下面的代碼
                       if (!focusTaken) {
                           if (mPerformClick == null) {
                               mPerformClick = new PerformClick();
                           }
                           if (!post(mPerformClick)) {
                               performClickInternal();
                           }
                       }
                    break;
                case MotionEvent.ACTION_DOWN:
                    //...down事件校驗(yàn)是都可以點(diǎn)擊,對(duì)長(zhǎng)按事件做了初始化现横,并且如果在滑動(dòng)控件內(nèi)部漓拾,對(duì)點(diǎn)擊事件做了延時(shí)響應(yīng)處理
                    break;
                case MotionEvent.ACTION_CANCEL:
                    //...cancel的時(shí)候也將點(diǎn)擊和長(zhǎng)按事件移除了
                    if (clickable) {
                        setPressed(false);
                    }
                    removeTapCallback();
                    removeLongPressCallback();
                    break;
                case MotionEvent.ACTION_MOVE:
                    //....move事件主要對(duì)點(diǎn)擊和長(zhǎng)按做了移除,比如當(dāng)手指移出了view等
                    break;
            }

            return true;
        }

        return false;
    }

在up事件中我們可以看到如果不是長(zhǎng)按事件那么就是執(zhí)行點(diǎn)擊事件

    public boolean performClick() {
        // We still need to call this method to handle the cases where performClick() was called
        // externally, instead of through performClickInternal()
        notifyAutofillManagerOnClick();

        final boolean result;
        final ListenerInfo li = mListenerInfo;
        //在這里執(zhí)行了點(diǎn)擊事件
        if (li != null && li.mOnClickListener != null) {
            playSoundEffect(SoundEffectConstants.CLICK);
            li.mOnClickListener.onClick(this);
            result = true;
        } else {
            result = false;
        }

        sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);

        notifyEnterOrExitForAutoFillIfNeeded(true);

        return result;
    }
事件處理的流程.png

事件分發(fā)

事件分發(fā)主要是由ViewGroup的dispatchTouchEvent來(lái)處理的
同樣的ViewGroup的dispatchTouchEvent的事件也分為Down戒祠,Move骇两,Up,Cancel
dispatchTouchEvent部分的主要邏輯

//是否攔截部分的邏輯
if (actionMasked == MotionEvent.ACTION_DOWN|| mFirstTouchTarget != null) {
    final Boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
    if (!disallowIntercept) {
        intercepted = onInterceptTouchEvent(ev);
        ev.setAction(action);
        // restore action in case it was changed
    } else {
        intercepted = false;
    }
} else {
    // There are no touch targets and this action is not an initial down
    // so this view group continues to intercept touches.
    intercepted = true;
}

//攔截部分的邏輯
Boolean alreadyDispatchedToNewTouchTarget = false;
if (!canceled && !intercepted) {
    // If the event is targeting accessibility focus we give it to the
    // view that has accessibility focus and if it does not handle it
    // we clear the flag and dispatch the event to all children as usual.
    // We are looking up the accessibility focused host to avoid keeping
    // state since these events are very rare.
    final int childrenCount = mChildrenCount;
    if (newTouchTarget == null && childrenCount != 0) {
        final float x = ev.getX(actionIndex);
        final float y = ev.getY(actionIndex);
        // Find a child that can receive the event.
        // Scan children from front to back.
        final ArrayList<View> preorderedList = buildTouchDispatchChildList();
        final View[] children = mChildren;
        for (int i = childrenCount - 1; i >= 0; i--) {
            final int childIndex = getAndVerifyPreorderedIndex(
                                                                childrenCount, i, customOrder);
            final View child = getAndVerifyPreorderedView(
                                                                preorderedList, children, childIndex);
            if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
                // Child wants to receive touch within its bounds.
                newTouchTarget = addTouchTarget(child, idBitsToAssign);
                alreadyDispatchedToNewTouchTarget = true;
                break;
            }
        }
    }
}

//自己處理或者分發(fā)的邏輯
// Dispatch to touch targets.
if (mFirstTouchTarget == null) {
    // No touch targets so treat this as an ordinary view.
    handled = dispatchTransformedTouchEvent(ev, canceled, null,
                            TouchTarget.ALL_POINTER_IDS);
} else {
    // Dispatch to touch targets, excluding the new touch target if we already
    // dispatched to it.  Cancel touch targets if necessary.
    TouchTarget predecessor = null;
    TouchTarget target = mFirstTouchTarget;
    while (target != null) {
        final TouchTarget next = target.next;
        if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
            handled = true;
        } else {
            final Boolean cancelChild = resetCancelNextUpFlag(target.child)
                                                    || intercepted;
            if (dispatchTransformedTouchEvent(ev, cancelChild,
                                                    target.child, target.pointerIdBits)) {
                handled = true;
            }
            if (cancelChild) {
                if (predecessor == null) {
                    mFirstTouchTarget = next;
                } else {
                    predecessor.next = next;
                }
                target.recycle();
                target = next;
                continue;
            }
        }
        predecessor = target;
        target = next;
    }
}

Down事件

  1. 當(dāng)為Down事件的時(shí)候根據(jù)disallowIntercept以及onInterceptTouchEvent來(lái)確定是否攔截事件
final Boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
    intercepted = onInterceptTouchEvent(ev);
    ev.setAction(action);
    // restore action in case it was changed
} else {
    intercepted = false;
}

默認(rèn)情況下 (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0為false

  • 如果onInterceptTouchEvent返回true那么intercepted就為true姜盈,那么意味著父控件要進(jìn)行攔截
  • 否則不進(jìn)行攔截
  1. 根據(jù)是否攔截來(lái)判斷是處理還是分發(fā)
if (!canceled && !intercepted)
  • 如果被攔截則不執(zhí)行上面的這個(gè)if內(nèi)部的邏輯
  • 如果沒(méi)有被攔截低千,則執(zhí)行上面的這個(gè)if內(nèi)部的邏輯
if (!canceled && !intercepted) {
    // If the event is targeting accessibility focus we give it to the
    // view that has accessibility focus and if it does not handle it
    // we clear the flag and dispatch the event to all children as usual.
    // We are looking up the accessibility focused host to avoid keeping
    // state since these events are very rare.
    final int childrenCount = mChildrenCount;
    if (newTouchTarget == null && childrenCount != 0) {
        final float x = ev.getX(actionIndex);
        final float y = ev.getY(actionIndex);
        // Find a child that can receive the event.
        // Scan children from front to back.
        final ArrayList<View> preorderedList = buildTouchDispatchChildList();
        final View[] children = mChildren;
        for (int i = childrenCount - 1; i >= 0; i--) {
            final int childIndex = getAndVerifyPreorderedIndex(
                                                                            childrenCount, i, customOrder);
            final View child = getAndVerifyPreorderedView(
                                                                            preorderedList, children, childIndex);
            if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
                // Child wants to receive touch within its bounds.
                newTouchTarget = addTouchTarget(child, idBitsToAssign);
                alreadyDispatchedToNewTouchTarget = true;
                break;
            }
        }
    }
}

在上面的代碼中我們可以看到對(duì)所有的子view進(jìn)行了遍歷,通過(guò)dispatchTransformedTouchEvent方法尋找有沒(méi)有子view要處理這個(gè)事件馏颂。

private Boolean dispatchTransformedTouchEvent(MotionEvent event, Boolean cancel,
            View child, int desiredPointerIdBits) {
    final Boolean handled;
    // Canceling motions is a special case.  We don't need to perform any transformations
    // or filtering.  The important part is the action, not the contents.
    final int oldAction = event.getAction();
    if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
        event.setAction(MotionEvent.ACTION_CANCEL);
        if (child == null) {
            handled = super.dispatchTouchEvent(event);
        } else {
            handled = child.dispatchTouchEvent(event);
        }
        event.setAction(oldAction);
        return handled;
    }
    // Perform any necessary transformations and dispatch.
    if (child == null) {
        handled = super.dispatchTouchEvent(transformedEvent);
    } else {
        final float offsetX = mScrollX - child.mLeft;
        final float offsetY = mScrollY - child.mTop;
        transformedEvent.offsetLocation(offsetX, offsetY);
        if (! child.hasIdentityMatrix()) {
            transformedEvent.transform(child.getInverseMatrix());
        }
        handled = child.dispatchTouchEvent(transformedEvent);
    }
    // Done.
    transformedEvent.recycle();
    return handled;
}

調(diào)用dispatchTransformedTouchEvent方法傳遞的參數(shù)是(ev, false, child, idBitsToAssign)示血,cancel為false,并且我們是Down事件饱亮,child不為null矾芙。所以他會(huì)執(zhí)行handled = child.dispatchTouchEvent(transformedEvent);在這里就是將Down事件分發(fā)給子view去處理了。

  • 如果child.dispatchTouchEvent返回了false近上,代表他不處理這個(gè)事件剔宪,那么就接著尋找下一個(gè)子View,看看是否處理這個(gè)事件
  • 如果child.dispatchTouchEvent返回了true壹无,代表他要處理這個(gè)事件
//這里將child加入到了TouchTarget的鏈表中葱绒,TouchTarget記錄了當(dāng)前有多少個(gè)手指觸摸,
//我們?cè)谶@里分析的是單點(diǎn)觸摸斗锭,那么這個(gè)鏈表應(yīng)該是只有一個(gè)數(shù)據(jù)
newTouchTarget = addTouchTarget(child, idBitsToAssign);
alreadyDispatchedToNewTouchTarget = true;
  1. 接著就執(zhí)行if (mFirstTouchTarget == null)判斷地淀,這里分為三種情況
  • 如果上面對(duì)Down事件進(jìn)行攔截,那么mFirstTouchTarget肯定為null岖是,執(zhí)行handled = dispatchTransformedTouchEvent(ev, canceled, null,TouchTarget.ALL_POINTER_IDS);
    我們發(fā)現(xiàn)在這里child為null帮毁,canceled也為false实苞,那么就會(huì)調(diào)用
    handled = super.dispatchTouchEvent(transformedEvent);去執(zhí)行View的dispatchTouchEvent去處理這個(gè)事件。
  • 如果沒(méi)有對(duì)Down事件進(jìn)行攔截烈疚,但是沒(méi)有子View去處理這個(gè)事件黔牵,那么他執(zhí)行的過(guò)程和攔截是一樣的
  • 如果沒(méi)有對(duì)Down事件進(jìn)行攔截,并且有子View進(jìn)行處理事件爷肝,這個(gè)時(shí)候mFirstTouchTarget就不為null執(zhí)行else代碼
TouchTarget predecessor = null;
TouchTarget target = mFirstTouchTarget;
while (target != null) {
    final TouchTarget next = target.next;
    if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
        handled = true;
    } else {
        final Boolean cancelChild = resetCancelNextUpFlag(target.child)
                                        || intercepted;
        if (dispatchTransformedTouchEvent(ev, cancelChild,
                                        target.child, target.pointerIdBits)) {
            handled = true;
        }
        if (cancelChild) {
            if (predecessor == null) {
                mFirstTouchTarget = next;
            } else {
                predecessor.next = next;
            }
            target.recycle();
            target = next;
            continue;
        }
    }
    predecessor = target;
    target = next;
}
設(shè)置標(biāo)記位.png

在分發(fā)的時(shí)候找到了子View的時(shí)候?qū)?biāo)記設(shè)置為true猾浦,并且將child加入到了TouchTarget中,那么他就會(huì)執(zhí)行handled = true;表示事件已經(jīng)被處理灯抛。


down事件分發(fā)過(guò)程.png

Move事件

  1. move事件同樣也會(huì)判斷是否進(jìn)行攔截金赦,因?yàn)閙FirstTouchTarget不為null
final Boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN
                    || mFirstTouchTarget != null) {
    final Boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
    if (!disallowIntercept) {
        intercepted = onInterceptTouchEvent(ev);
        ev.setAction(action);
        // restore action in case it was changed
    } else {
        intercepted = false;
    }
} else {
    // There are no touch targets and this action is not an initial down
    // so this view group continues to intercept touches.
    intercepted = true;
}
  • 如果被攔截的話就不會(huì)再分發(fā)
  • 如果沒(méi)有被攔截,會(huì)遍歷所有的子View進(jìn)行分發(fā)对嚼,看看是不是有子View要處理這個(gè)事件
  1. 根據(jù)是否攔截以及是否有子View處理事件夹抗,對(duì)事件進(jìn)行處理
if (mFirstTouchTarget == null) {
    // No touch targets so treat this as an ordinary view.
    handled = dispatchTransformedTouchEvent(ev, canceled, null,
                            TouchTarget.ALL_POINTER_IDS);
} else {
    // Dispatch to touch targets, excluding the new touch target if we already
    // dispatched to it.  Cancel touch targets if necessary.
    TouchTarget predecessor = null;
    TouchTarget target = mFirstTouchTarget;
    while (target != null) {
        final TouchTarget next = target.next;
        if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
            handled = true;
        } else {
            final Boolean cancelChild = resetCancelNextUpFlag(target.child)
                                            || intercepted;
            if (dispatchTransformedTouchEvent(ev, cancelChild,
                                            target.child, target.pointerIdBits)) {
                handled = true;
            }
            if (cancelChild) {
                if (predecessor == null) {
                    mFirstTouchTarget = next;
                } else {
                    predecessor.next = next;
                }
                target.recycle();
                target = next;
                continue;
            }
        }
        predecessor = target;
        target = next;
    }
}
  • 如果事件被攔截,mFirstTouchTarget不為null猪半,那么會(huì)執(zhí)行else兔朦,調(diào)用了dispatchTransformedTouchEvent,參數(shù)為(ev, cancelChild,target.child, target.pointerIdBits)),cancelChild為true磨确,target.child不為null
private Boolean dispatchTransformedTouchEvent(MotionEvent event, Boolean cancel,
            View child, int desiredPointerIdBits) {
    final Boolean handled;
    // Canceling motions is a special case.  We don't need to perform any transformations
    // or filtering.  The important part is the action, not the contents.
    final int oldAction = event.getAction();
    if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
        event.setAction(MotionEvent.ACTION_CANCEL);
        if (child == null) {
            handled = super.dispatchTouchEvent(event);
        } else {
            handled = child.dispatchTouchEvent(event);
        }
        event.setAction(oldAction);
        return handled;
    }
    // Perform any necessary transformations and dispatch.
    if (child == null) {
        handled = super.dispatchTouchEvent(transformedEvent);
    } else {
        final float offsetX = mScrollX - child.mLeft;
        final float offsetY = mScrollY - child.mTop;
        transformedEvent.offsetLocation(offsetX, offsetY);
        if (! child.hasIdentityMatrix()) {
            transformedEvent.transform(child.getInverseMatrix());
        }
        handled = child.dispatchTouchEvent(transformedEvent);
    }
    return handled;
}

做了兩步處理沽甥,第一步event.setAction(MotionEvent.ACTION_CANCEL);將事件設(shè)置為cancel,第二步調(diào)用handled = child.dispatchTouchEvent(event);將cancel事件分發(fā)給子View乏奥。

  • 事件沒(méi)有被攔截摆舟,但是沒(méi)有子View消費(fèi)這個(gè)事件,這個(gè)時(shí)候mFirstTouchTarget不為null邓了,會(huì)執(zhí)行dispatchTransformedTouchEvent(ev, cancelChild,target.child, target.pointerIdBits)方法恨诱,其中的參數(shù)是cancelChild為false,target.child為null骗炉,那么會(huì)執(zhí)行handled = super.dispatchTouchEvent(transformedEvent);調(diào)用View的dispatchTouchEvent來(lái)處理事件照宝。
  • 事件沒(méi)有被攔截,有子View消費(fèi)事件句葵,這個(gè)時(shí)候mFirstTouchTarget不為null,執(zhí)行handled = true;將事件消費(fèi)厕鹃。
if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
    handled = true;
}

會(huì)有多個(gè)move事件,分發(fā)流程和上面是一樣的乍丈。


move事件分發(fā)流程.png

滑動(dòng)沖突解決

  1. 內(nèi)部攔截法
    requestDisallowInterceptTouchEvent剂碴,true代表讓父親不要攔截
    false 代表讓父親攔截
  2. 外部攔截法
    onInterceptTouchEvent,返回true則會(huì)攔截轻专,返回false則不會(huì)攔截
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末忆矛,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子请垛,更是在濱河造成了極大的恐慌催训,老刑警劉巖洽议,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異漫拭,居然都是意外死亡绞铃,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門嫂侍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人荚坞,你說(shuō)我怎么就攤上這事挑宠。” “怎么了颓影?”我有些...
    開封第一講書人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵各淀,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我诡挂,道長(zhǎng)碎浇,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任璃俗,我火速辦了婚禮奴璃,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘城豁。我一直安慰自己苟穆,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開白布唱星。 她就那樣靜靜地躺著雳旅,像睡著了一般。 火紅的嫁衣襯著肌膚如雪间聊。 梳的紋絲不亂的頭發(fā)上攒盈,一...
    開封第一講書人閱讀 49,144評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音哎榴,去河邊找鬼型豁。 笑死,一個(gè)胖子當(dāng)著我的面吹牛叹话,可吹牛的內(nèi)容都是我干的偷遗。 我是一名探鬼主播,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼驼壶,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼氏豌!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起热凹,我...
    開封第一講書人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤泵喘,失蹤者是張志新(化名)和其女友劉穎泪电,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體纪铺,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡相速,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了鲜锚。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片突诬。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖芜繁,靈堂內(nèi)的尸體忽然破棺而出旺隙,到底是詐尸還是另有隱情,我是刑警寧澤骏令,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布蔬捷,位于F島的核電站,受9級(jí)特大地震影響榔袋,放射性物質(zhì)發(fā)生泄漏周拐。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一凰兑、第九天 我趴在偏房一處隱蔽的房頂上張望妥粟。 院中可真熱鬧,春花似錦聪黎、人聲如沸罕容。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)锦秒。三九已至,卻和暖如春喉镰,著一層夾襖步出監(jiān)牢的瞬間旅择,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工侣姆, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留生真,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓捺宗,卻偏偏與公主長(zhǎng)得像柱蟀,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子蚜厉,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345