03-深度理解-Android事件分发机制

你确定你对Android事件分发机制了然于胸了吗?如果能麻溜的回答出下面几个问题,还请大佬绕道,如果不能,请听我细细讲解:

1.View事件分发机制的传递过程是?onTouch,onTouchEvent,onClick的执行顺序?

2.DOWN事件分发机制的传递过程?遇到过滑动冲突吗?怎么解决的?

3.MOVE事件分发机制的传递过程?

4.事件分发机制流程及优缺点?

1. View事件分发机制的传递过程

1.1 View#dispatchTouchEvent:onTouch

public boolean dispatchTouchEvent(MotionEvent event) {

    // 事件消费默认返回false

    boolean result = false;

    // 触摸事件安全过滤

    if (onFilterTouchEventForSecurity(event)) {

        // 判断有没有注册onTouchListener,onTouch事件是否被消费

        // 如果事件被onTouch消费就不会继续往下执行;

        // 如果没有被消费,继续执行onTouchEvent方法。

        ListenerInfo li = mListenerInfo;

        if (li != null && li.mOnTouchListener != null

                && (mViewFlags & ENABLED_MASK) == ENABLED

                && li.mOnTouchListener.onTouch(this, event)) {

            result = true;

        }

        // 如果onTouchEvent事件被消费,则事件被消费;

        // 如果onTouchEvent事件没有被消费,那么事件依然会继续往下传递。

        if (!result && onTouchEvent(event)) {

            result = true;

        }

    }

    return result;

}

1.2 View#onTouchEvent:onTouchEvent

public boolean onTouchEvent(MotionEvent event) {

    // 判断是否注册点击事件

    final boolean clickable = ((viewFlags & CLICKABLE) == CLICKABLE

            || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)

            || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE;

    // 有点击事件判断action

    if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {

        switch (action) {

            case MotionEvent.ACTION_UP:

                // ACTION_UP事件触发时,执行performClick方法。

                performClick();

                break;

            case MotionEvent.ACTION_DOWN:

                break;

            case MotionEvent.ACTION_CANCEL:

                break;

            case MotionEvent.ACTION_MOVE:

                break;

        }

        return true;

    }

    return false;

}

1.3 View#performClick:onClick

public boolean performClick() {

    final boolean result;

    final ListenerInfo li = mListenerInfo;

    // 如果设置了onClickListener,就会执行onClick方法,事件就会被消费。

    if (li != null && li.mOnClickListener != null) {

        playSoundEffect(SoundEffectConstants.CLICK);

        li.mOnClickListener.onClick(this);

        result = true;

    } else {

        result = false;

    }

    return result;

}

小贴士:

View没有onInterceptTouchEvent方法,当有点击事件传递给它时,就会执行onTouchEvent方法。

onTouchEvent方法:默认消耗事件,但是如果View不可点击就不会消耗事件。

onClick事件会发生的前提:当前View可点击,并且收到了down和up事件。

方法执行顺序:onTouch-onTouchEvent-onClick

思考:

一个需求:onTouch返回true要执行onClick。

解决办法:在onTouch方法里ACTION_DOWN里加v.performClick();

场景:上层popupwindow挡住了下层view的点击事件

上层popupwindow设置isTouchable=false

2.DOWN事件分发机制的传递过程:

2.1 Activity#dispatchTouchEvent:

当我们点击屏幕时,触发MotionEvent.ACTION_DOWN事件,此时屏幕处于Activity界面,Activity#dispatchTouchEvent方法进行事件分发:


public boolean dispatchTouchEvent(MotionEvent ev) {

    if (ev.getAction() == MotionEvent.ACTION_DOWN) {

        // 执行和用户交互的操作

        // 例如聪明的管理状态栏通知,帮助activity在正确的时间取消通知

        onUserInteraction();

    }

    // 将事件传递给Window,Window#superDispatchTouchEvent进行事件分发

    if (getWindow().superDispatchTouchEvent(ev)) {

        // Window消费了该事件,直接return退出该方法

        return true;

    }

4

    // Window没有消费该事件,继续由Activity#onTouchEvent方法来处理

    return onTouchEvent(ev);

}

小贴士:

return的用法:

结束该方法,继续执行方法后面的语句。

2.2 Window#superDispatchTouchEvent:

public abstract boolean superDispatchTouchEvent(MotionEvent event);

Window类:抽象类,superDispatchTouchEvent是抽象方法。

看Window类的介绍,这个抽象类的唯一存在实现是PhoneWindow。

双击shift,搜索PhoneWindow类。

PhoneWindow#superDispatchTouchEvent:


@Override

public boolean superDispatchTouchEvent(MotionEvent event) {

    // PhoneWindow将事件传递给了DecorView

    return mDecor.superDispatchTouchEvent(event);

}

2.3 DecorView#superDispatchTouchEvent:

public boolean superDispatchTouchEvent(MotionEvent event) {

    // DecorView将事件传递给了ViewGroup

    return super.dispatchTouchEvent(event);

}

2.4 ViewGroup#dispatchTouchEvent:

@Override

// 事件分发:dispatchTouchEvent

public boolean dispatchTouchEvent(MotionEvent ev) {

    boolean handled = false;

    if (onFilterTouchEventForSecurity(ev)) {

        // 注释1

        // 初始化的原因:一个完整的事件序列是以DOWN开始,以UP结束的。

        // 如果是DOWN事件,说明这是一个新的事件序列,所以需要初始化之前的状态。

        if (actionMasked == MotionEvent.ACTION_DOWN) {

            // 清除触摸目标

            cancelAndClearTouchTargets(ev);

            // 重置触摸状态

            // mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT代码意思是:将FLAG_DISALLOW_INTERCEPT标志位设置为false。  

            resetTouchState();

        }

        // 注释2

        final boolean intercepted;

        if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) {

            // 因为初始化,所以该值为false

            final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;

            if (!disallowIntercept) {

                // 事件拦截:onInterceptTouchEvent

                // onInterceptTouchEvent返回true,intercepted为true;

                // onInterceptTouchEvent返回false,intercepted为false。

                intercepted = onInterceptTouchEvent(ev);

            } else {

                intercepted = false;

            }

        } else {

            intercepted = true;

        }

       // 注释3

        final boolean canceled = resetCancelNextUpFlag(this) || actionMasked == MotionEvent.ACTION_CANCEL;

        // 注释4

        TouchTarget newTouchTarget = null;

        if (!canceled && !intercepted) {

            if (actionMasked == MotionEvent.ACTION_DOWN

                    || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)

                    || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {

                final int childrenCount = mChildrenCount;

                if (newTouchTarget == null && childrenCount != 0) {

                    final ArrayList<View> preorderedList = buildTouchDispatchChildList();

                    for (int i = childrenCount - 1; i >= 0; i--) {

                        // 注释5

                        final int childIndex = getAndVerifyPreorderedIndex(

                                childrenCount, i, customOrder);

                        final View child = getAndVerifyPreorderedView(

                                preorderedList, children, childIndex);

                        // 注释6

                        if (!canViewReceivePointerEvents(child)

                                || !isTransformedTouchPointInView(x, y, child, null)) {

                            ev.setTargetAccessibilityFocus(false);

                            continue;

                        }

                        // 注释7

                        newTouchTarget = getTouchTarget(child);

                        if (newTouchTarget != null) {

                            newTouchTarget.pointerIdBits |= idBitsToAssign;

                            break;

                        }

                        // 注释8

                        resetCancelNextUpFlag(child);

                        if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {

                            if (preorderedList != null) {

                                for (int j = 0; j < childrenCount; j++) {

                                    if (children[childIndex] == mChildren[j]) {

                                        mLastTouchDownIndex = j;

                                        break;

                                    }

                                }

                            } else {

                                mLastTouchDownIndex = childIndex;

                            }

                            newTouchTarget = addTouchTarget(child, idBitsToAssign);

                            alreadyDispatchedToNewTouchTarget = true;

                            break;

                        }

                        ev.setTargetAccessibilityFocus(false);

                    }

                    if (preorderedList != null) preorderedList.clear();

                }

                if (newTouchTarget == null && mFirstTouchTarget != null) {

                    newTouchTarget = mFirstTouchTarget;

                    while (newTouchTarget.next != null) {

                        newTouchTarget = newTouchTarget.next;

                    }

                    newTouchTarget.pointerIdBits |= idBitsToAssign;

                }

            }

        }

        // 注释9:

        if (mFirstTouchTarget == null) {

            handled = dispatchTransformedTouchEvent(ev, canceled, null,

                    TouchTarget.ALL_POINTER_IDS);

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

            }

        }

        // 注释10

        if (canceled

                || actionMasked == MotionEvent.ACTION_UP

                || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {

            resetTouchState();

        } else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) {

            final int actionIndex = ev.getActionIndex();

            final int idBitsToRemove = 1 << ev.getPointerId(actionIndex);

            removePointersFromTouchTargets(idBitsToRemove);

        }

    }

    return handled;

}

Down事件:

注释1:见代码。

注释2:

当onInterceptTouchEvent返回true,intercepted为true,执行注释9。

mFirstTouchTarget为空,没有进行任何赋值,执行dispatchTransformedTouchEvent方法。

dispatchTransformedTouchEvent:

canceled值:看注释3#resetCancelNextUpFlag默认为false,且没有执行ACTION_CANCEL事件,所以值为false。


private static boolean resetCancelNextUpFlag(@NonNull View view) {

    // 如果if条件boolean类型为true,条件置为false,该方法返回true

    if ((view.mPrivateFlags & PFLAG_CANCEL_NEXT_UP_EVENT) != 0) {

        view.mPrivateFlags &= ~PFLAG_CANCEL_NEXT_UP_EVENT;

        return true;

    }

    // 该方法默认返回false

    return false;

}

dispatchTransformedTouchEvent:cancel为false,child为null,执行注释2:


private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,

                                              View child, int desiredPointerIdBits) {

    final boolean handled;

    final int oldAction = event.getAction();

    // 注释1

    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;

    }

    // 注释2

    if (child == null) {

        // 执行View#dispatchTouchEvent

        // super关键字:用来调用超类里的方法,ViewGroup继承View。

        // 事件交给父容器来处理。

        handled = super.dispatchTouchEvent(transformedEvent);

    } else {

        handled = child.dispatchTouchEvent(transformedEvent);

    }

    return handled;

}

当onInterceptTouchEvent返回false,intercepted为false,假设canceled依然为false,执行注释4。

执行DOWN事件,newTouchTarget为空,mChildrenCount为ViewGroup中所有子View的数量不为0,执行buildTouchDispatchChildList方法:


public ArrayList<View> buildTouchDispatchChildList() {

    // 对事件进行排序

    return buildOrderedChildList();

}

buildOrderedChildList:将子视图从ViewGroup到View(比如Button)从前往后依次有序添加到list集合中,Z轴不断增大。


ArrayList<View> buildOrderedChildList() {

    if (mPreSortedChildren == null) {

        mPreSortedChildren = new ArrayList<>(childrenCount);

    } else {

        mPreSortedChildren.clear();

        mPreSortedChildren.ensureCapacity(childrenCount);

    }

    for (int i = 0; i < childrenCount; i++) {

        final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);

        final View nextChild = mChildren[childIndex];

        final float currentZ = nextChild.getZ();

        int insertIndex = i;

        while (insertIndex > 0 && mPreSortedChildren.get(insertIndex - 1).getZ() > currentZ) {

            insertIndex--;

        }

        mPreSortedChildren.add(insertIndex, nextChild);

    }

    return mPreSortedChildren;

}

回到注释4:执行for循环。

注释5:从后往前第一个得到的View为Button。

注释6:判断View是否能收到点击事件和点是否在它的坐标里。如果不能收到点击事件或者不在坐标里,继续。

注释7:getTouchTarget为空值,执行注释8。

注释8:

执行dispatchTransformedTouchEvent方法,child不为空,执行child#dispatchTouchEvent方法即View#dispatchTouchEvent方法。即Button在处理事件。如果Button处理了事件,执行注释8的if里的内容。先拿到所有点击事件的索引,然后用addTouchTarget方法将child赋值给mFirstTouchTarget,newTouchTarget为mFirstTouchTarget,alreadyDispatchedToNewTouchTarget赋值为true。最后执行break退出整个for循环。

注意:

如果注释8的View没有把事件消费掉,那么剩余的Activity,Window,ViewGroup就会一直调用dispatchTransformTouchEvent处理事件直到把事件消费为止。只要子View处理了事件,父View就再也不会接收到事件。

注释9:

mFirstTouchTarget不为空,target.next为空,handled为true。

小贴士:

二进制和按位操作符:& | ~

break:退出for循环。

2.5 ViewGroup#requestDisallowInterceptedTouchEvent:

定义:子View通过调用父View的requestDisallowInterceptTouchEvent方法来要求父View不拦截事件。

即:子View调用getParent().requestDisallowInterceptTouchEvent();

requestDisallowInterceptTouchEvent传的值true,disallowIntercept为true。

当为DOWN事件时,ViewGroup#dispatchTouchEvent中注释2处intercepted为false,会执行注释4,向子View传递。


@Override

public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {

    if (disallowIntercept == ((mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0)) {

        return;

    }

    if (disallowIntercept) {

        mGroupFlags |= FLAG_DISALLOW_INTERCEPT;

    } else {

        mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;

    }

    if (mParent != null) {

        mParent.requestDisallowInterceptTouchEvent(disallowIntercept);

    }

}

标志位FLAG_DISALLOW_INTERCEPT的作用:

要求父View不拦截事件,向子View传递。

DOWN事件除外。因为如果是ACTION_DOWN事件,每次都会在注释1处重置手势状态。

小结:

DOWN事件的流程:

事件分发从父容器向子容器分发。

事件分发:dispatchTouchEvent

事件拦截:onInterceptTouchEvent。

为true不执行注释4,事件由父容器处理;

为false执行注释4,向子View传递,发生到ViewGroup时,先问Button处不处理该事件,不处理就交给ViewGroup,再根据for循环一层一层向上处理。

ViewGroup想要拦截事件,可以重写onInterceptTouchEvent方法返回true。

事件排序:buildTouchDispatchChildList,是为了从下往上排序处理。

事件处理:dispatchTransformedTouchEvent

事件消费:onTouchEvent

流程图:

[图片上传失败...(image-39be25-1692928698844)])

问题:

父View#onInterceptedTouchEvent设置为true,

DOWN事件requestDisallowInterceptedTouchEvent为true不起作用,还是会被父View拦截。

原因:

DOWN事件会在注释1处进行初始化,清除状态,导致disallowIntercept为false执行onInterceptTouchEvent方法为true被父View拦截。

解决:(重点)

父View#onInterceptedTouchEvent:

当点击事件为ACTION_DOWN时,return false,向子View传递。

如果onInterceptedTouchEvent是重写的方法,还要写方法super.onInterceptTouchEvent。

3.MOVE事件分发机制的传递过程:

执行完DOWN事件就开始执行MOVE事件。

3.1 ViewGroup#dispatchTouchEvent:

@Override

public boolean dispatchTouchEvent(MotionEvent ev) {

    boolean handled = false;

    if (onFilterTouchEventForSecurity(ev)) {

        // 注释1:不执行

        if (actionMasked == MotionEvent.ACTION_DOWN) {

            cancelAndClearTouchTargets(ev);

            resetTouchState();

        }

        // 注释2:if 语句第二个条件mFirstTouchTarget不为空满足

        final boolean intercepted;

        if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) {

            final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;

            if (!disallowIntercept) {

                // 事件拦截:onInterceptTouchEvent

                intercepted = onInterceptTouchEvent(ev);

            } else {

                intercepted = false;

            }

        } else {

            intercepted = true;

        }

       // 注释3

        final boolean canceled = resetCancelNextUpFlag(this) || actionMasked == MotionEvent.ACTION_CANCEL;

        // 注释4

        TouchTarget newTouchTarget = null;

        // 第一个 if 语句

        if (!canceled && !intercepted) {

            // 第二个 if 语句

            if (actionMasked == MotionEvent.ACTION_DOWN

                    || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)

                    || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {

                final int childrenCount = mChildrenCount;

                if (newTouchTarget == null && childrenCount != 0) {

                    final ArrayList<View> preorderedList = buildTouchDispatchChildList();

                    for (int i = childrenCount - 1; i >= 0; i--) {

                        // 注释5

                        final int childIndex = getAndVerifyPreorderedIndex(

                                childrenCount, i, customOrder);

                        final View child = getAndVerifyPreorderedView(

                                preorderedList, children, childIndex);

                        // 注释6

                        if (!canViewReceivePointerEvents(child)

                                || !isTransformedTouchPointInView(x, y, child, null)) {

                            ev.setTargetAccessibilityFocus(false);

                            continue;

                        }

                        // 注释7

                        newTouchTarget = getTouchTarget(child);

                        if (newTouchTarget != null) {

                            newTouchTarget.pointerIdBits |= idBitsToAssign;

                            break;

                        }

                        // 注释8

                        resetCancelNextUpFlag(child);

                        if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {

                            if (preorderedList != null) {

                                for (int j = 0; j < childrenCount; j++) {

                                    if (children[childIndex] == mChildren[j]) {

                                        mLastTouchDownIndex = j;

                                        break;

                                    }

                                }

                            } else {

                                mLastTouchDownIndex = childIndex;

                            }

                            newTouchTarget = addTouchTarget(child, idBitsToAssign);

                            alreadyDispatchedToNewTouchTarget = true;

                            break;

                        }

                        ev.setTargetAccessibilityFocus(false);

                    }

                    if (preorderedList != null)

                        preorderedList.clear();

                }

                if (newTouchTarget == null && mFirstTouchTarget != null) {

                    newTouchTarget = mFirstTouchTarget;

                    while (newTouchTarget.next != null) {

                        newTouchTarget = newTouchTarget.next;

                    }

                    newTouchTarget.pointerIdBits |= idBitsToAssign;

                }

            }

        }

        // 注释9:

        // 第一个 if-else 语句

        if (mFirstTouchTarget == null) {

            handled = dispatchTransformedTouchEvent(ev, canceled, null,

                    TouchTarget.ALL_POINTER_IDS);

        } else {

            TouchTarget predecessor = null;

            TouchTarget target = mFirstTouchTarget;

            while (target != null) {

                final TouchTarget next = target.next;

                // 第二个 if-else 语句

                if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {

                    handled = true;

                } else {

                    final boolean cancelChild = resetCancelNextUpFlag(target.child)

                            || intercepted;

// 第三个 if 语句

                    if (dispatchTransformedTouchEvent(ev, cancelChild,

                            target.child, target.pointerIdBits)) {

                        handled = true;

                    }

// 第四个 if 语句

                    if (cancelChild) {

                        if (predecessor == null) {

                            mFirstTouchTarget = next;

                        } else {

                            predecessor.next = next;

                        }

                        target.recycle();

                        target = next;

                        continue;

                    }

                }

                predecessor = target;

                target = next;

            }

        }

        // 注释10

        if (canceled

                || actionMasked == MotionEvent.ACTION_UP

                || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {

            resetTouchState();

        } else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) {

            final int actionIndex = ev.getActionIndex();

            final int idBitsToRemove = 1 << ev.getPointerId(actionIndex);

            removePointersFromTouchTargets(idBitsToRemove);

        }

    }

    return handled;

}

默认:

注释2:onInterceptTouchEvent默认返回false,执行注释4。

注释4:第一个 if 语句满足,第二个 if 语句 不满足,执行注释9。

注释9:

第一个 if-else 语句,mFirstTouchTarget是全局属性,在DOWN事件后不为空,执行else。

第二个 if-else 语句,newTouchTarget是局部属性,没有执行注释4里的语句,所以为空,执行else。

第三个 if 语句,执行dispatchTransformedTouchEvent方法,cancel为false,child不为空,执行方法里注释2的else语句即child.dispatchTouchEvent方法。

第一次MOVE,父View拦截子View:

注释2:假设onInterceptTouchEvent返回true,不执行注释4,执行注释9。

注释9:

第一个 if-else 语句,同上。

第二个 if-else 语句,同上。

第三个 if 语句:

  • 执行dispatchTransformedTouchEvent方法,cancel为true,child不为空,执行方法里的注释1。

  • MotionEvent事件设置为了ACTION_CANCEL,调用child.dispatchTouchEvent方法,但是最后事件还是oldAction即MOVE事件。

重点:此处child取消了事件。

第四个 if 语句:将mFirstTouchTarget置为了空。

第二次MOVE:

MOVE事件会多次触发,再从执行MOVE事件:

注释2:执行 else 语句 intercepted = true 部分。

注释4:不执行

注释9:执行第一个 if-else 语句的 if 语句,即 dispatchTransformedTouchEvent方法,cancel为false,child为空,执行方法里注释2的else语句super.dispatchTouchEvent即父View它自己的dispatchTouchEvent。

流程图:

[图片上传失败...(image-f7701e-1692928698844)])

MotionEvent中的四个事件:

ACTION_DOWN:手指初次接触到屏幕时触发

ACTION_MOVE:手指在屏幕上滑动时触发,会多次触发

ACTION_UP:手指离开屏幕时触发

ACTION_CANCEL:事件被上层拦截时触发

4.事件分发机制流程及优缺点:

事件分发机制其实是一个责任链模式,上级交给下级,下级完成不了再交给上级。但是这样做的缺点是层级太多,不够扁平化。

[图片上传失败...(image-5d1707-1692928698844)])

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,841评论 5 472
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,415评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,904评论 0 333
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,051评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,055评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,255评论 1 278
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,729评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,377评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,517评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,420评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,467评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,144评论 3 317
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,735评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,812评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,029评论 1 256
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,528评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,126评论 2 341

推荐阅读更多精彩内容