Android 事件沖突處理

概述

本文主要分享Android常見的事件沖突處理烂完,處理方式有兩種:

  • 外部攔截:父容器處理沖突
  • 內(nèi)部攔截:子控件處理沖突

在介紹這兩種處理方法之前祸挪,我們必須先了解兩件事情:

  • 事件在控件中是如何傳遞的
  • 事件沖突產(chǎn)生的根本原因

事件在控件中是如何傳遞的

先來看一張事件分發(fā)的大致流程圖:

通過流程圖可知套耕,事件的分發(fā)是從Activity的dispatchTouchEvent開始傳遞的丰刊,然后調(diào)用PhoneWindow的superDispatchTouchEvent澜躺,再調(diào)用DecorView的superDispatchTouchEvent桩蓉,再調(diào)用到ViewGroup的dispatchTouchEvent方法淋纲,ViewGroup要先走分發(fā)流程,再走處理流程院究,而View只能走處理流程洽瞬。下面便從ViewGroup的dispatchTouchEvent方法分析事件的傳遞流程。

DOWN事件

事件的分發(fā)是從Down事件開始的业汰,Down事件只有一個(gè)片任,ViewGroup的dispatchTouchEvent方法對(duì)Down事件的處理方式有以下兩種:

  • 攔截事件
  • 不攔截事件

接下來結(jié)合源碼分析這兩種處理方式有什么區(qū)別。

攔截事件

跟蹤ViewGroup中dispatchTouchEvent方法針對(duì)ACTION_DOWN處理的關(guān)鍵代碼:

    if (actionMasked == MotionEvent.ACTION_DOWN) {
        cancelAndClearTouchTargets(ev);
        resetTouchState();
    }

    // Check for interception.
    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;
    }
    ...
    if (mFirstTouchTarget == null) {
        // No touch targets so treat this as an ordinary view.
        handled = dispatchTransformedTouchEvent(ev, canceled, null,
                TouchTarget.ALL_POINTER_IDS);
    }

上述中有一句關(guān)鍵代碼:
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
disallowIntercept表示是否不允許父控件攔截蔬胯,由于在MotionEvent.ACTION_DOWN中調(diào)用了resetTouchState方法:

private void resetTouchState() {
    clearTouchTargets();
    resetCancelNextUpFlag(this);
    mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
    mNestedScrollAxes = SCROLL_AXIS_NONE;
}

所以在MotionEvent.ACTION_DOWN時(shí)disallowIntercept的值為fasle,此時(shí)會(huì)調(diào)用ViewGroup的onInterceptTouchEvent对供,因?yàn)閿r截了Down事件,所以onInterceptTouchEvent返回true,此時(shí)事件停止向子控件分發(fā)氛濒,交給自身處理即mFirstTouchTarget==null,然后會(huì)調(diào)用到以下的關(guān)鍵代碼:
handled = dispatchTransformedTouchEvent(ev, canceled, null, TouchTarget.ALL_POINTER_IDS)

跟蹤dispatchTransformedTouchEvent方法的關(guān)鍵代碼:

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

由源碼可知产场,由于child==null會(huì)調(diào)用到 super.dispatchTouchEvent方法,即調(diào)用到View的dispatchTouchEvent方法舞竿,關(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;
        }
    }

由源碼可知京景,View的dispatchTouchEvent中會(huì)根據(jù)mOnTouchListener.onTouch或onTouchEvent是否返回true,來判斷是否消費(fèi)該事件骗奖。到這里攔截事件的基本流程就結(jié)束了确徙。
這里補(bǔ)充一個(gè)小知識(shí)點(diǎn)醒串,由于mOnTouchListener.onTouch是優(yōu)先與onTouchEvent,所以當(dāng)mOnTouchListener.onTouch返回true時(shí)鄙皇,以下代碼不會(huì)執(zhí)行:

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

那如果此時(shí)控件同時(shí)設(shè)置了onClick事件便會(huì)失效芜赌,因?yàn)樵趏nTouchEvent的ACTION_UP事件中調(diào)用了 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);

      notifyEnterOrExitForAutoFillIfNeeded(true);

      return result;
  }

不攔截事件

ViewGroup的onInterceptTouchEvent返回false(默認(rèn)返回false)時(shí),此時(shí)會(huì)將事件分發(fā)給子控件處理伴逸,如果子控件都不處理則自己處理該事件缠沈,關(guān)鍵代碼如下:

    boolean alreadyDispatchedToNewTouchTarget = false;
    if (!canceled && !intercepted) {
               
        View childWithAccessibilityFocus = ev.isTargetAccessibilityFocus()
                ? findChildWithAccessibilityFocus() : null;
    
        if (actionMasked == MotionEvent.ACTION_DOWN
                || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
                || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
            final int actionIndex = ev.getActionIndex(); // always 0 for down
            final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
                    : TouchTarget.ALL_POINTER_IDS;
    
            removePointersFromTouchTargets(idBitsToAssign);
    
            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 boolean customOrder = preorderedList == null
                        && isChildrenDrawingOrderEnabled();
                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 (childWithAccessibilityFocus != null) {
                        if (childWithAccessibilityFocus != child) {
                            continue;
                        }
                        childWithAccessibilityFocus = null;
                        i = childrenCount - 1;
                    }
    
                    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);
                    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;
                    }
    
                    // The accessibility focus didn't handle the event, so clear
                    // the flag and do a normal dispatch to all children.
                    ev.setTargetAccessibilityFocus(false);
                }
                if (preorderedList != null) preorderedList.clear();
            }
    
            if (newTouchTarget == null && mFirstTouchTarget != null) {
                // Did not find a child to receive the event.
                // Assign the pointer to the least recently added target.
                newTouchTarget = mFirstTouchTarget;
                while (newTouchTarget.next != null) {
                    newTouchTarget = newTouchTarget.next;
                }
                newTouchTarget.pointerIdBits |= idBitsToAssign;
            }
        }
    }

遍歷子控件集合時(shí),會(huì)根據(jù)子控件的dispatchTransformedTouchEvent方法判斷是否有子控件處理了事件错蝴,若有子控件處理洲愤,會(huì)執(zhí)行如關(guān)鍵代碼:

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

這里采用了鏈表來存儲(chǔ)目標(biāo)控件,此時(shí)mFirstTouchTarget不為空顷锰,那如果子控件都沒有處理柬赐,是如何將事件再交給父控件處理呢?繼續(xù)跟蹤源碼:

if (mFirstTouchTarget == null) {
        // No touch targets so treat this as an ordinary view.
        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;
        }
    }

當(dāng)沒有子控件都沒有處理事件時(shí)官紫,mFirstTouchTarget=null,此時(shí)會(huì)調(diào)用ViewGroup的dispatchTransformedTouchEvent方法自己處理該事件躺率,如果有子控件處理,會(huì)執(zhí)行以下判斷:

     if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
        handled = true;
    }

因?yàn)橛凶涌丶幚硗蚍藭r(shí)alreadyDispatchedToNewTouchTarget = true、mFirstTouchTarget=newTouchTarget慎框、 target.next=null即滿足上述條件良狈,while循環(huán)只會(huì)執(zhí)行一次,到這里不攔截事件的基本流程就結(jié)束了笨枯。

MOVE事件

Move事件的傳遞也是通過以下兩種方式進(jìn)行分析:

  • 不攔截Move事件傳遞
  • 攔截Move事件傳遞

Move事件正常傳遞

父控件不攔截事件時(shí)薪丁,Move事件的傳遞也是通過dispatchTouchEvent方法傳遞給目標(biāo)控件的,關(guān)鍵代碼如下:

    boolean alreadyDispatchedToNewTouchTarget = false;
    View childWithAccessibilityFocus = ev.isTargetAccessibilityFocus()
                        ? findChildWithAccessibilityFocus() : null;

                if (actionMasked == MotionEvent.ACTION_DOWN
                        || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
                        || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
                        .....
                }//此時(shí)是Move事件馅精,不會(huì)執(zhí)行這段代碼
    }
    .....
    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í)alreadyDispatchedToNewTouchTarget=false,所以會(huì)走else分支严嗜,會(huì)執(zhí)行以下關(guān)鍵代碼:

 if (dispatchTransformedTouchEvent(ev, cancelChild,
        target.child, target.pointerIdBits)) {
    handled = true;
}

將Move事件交給對(duì)應(yīng)的目標(biāo)控件(Down事件保存的Target),到這里正常的Move事件就執(zhí)行完了洲敢。

攔截Move事件傳遞

分析攔截事件時(shí)漫玄,先來看一段代碼:

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

通過上面Down事件分析可知,由于Down事件做了重置操作压彭,所以disallowIntercept的值為false,即if分支的代碼一定會(huì)執(zhí)行睦优,此時(shí)攔截子控件的Move事件,會(huì)執(zhí)行以下關(guān)鍵代碼:

    final boolean cancelChild = resetCancelNextUpFlag(target.child)
                                    || intercepted;
    if (dispatchTransformedTouchEvent(ev, cancelChild,
            target.child, target.pointerIdBits)) {
        handled = true;
    }

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

因?yàn)閿r截了move事件壮不,此時(shí)intercepted=true, cancelChild=true,此時(shí)會(huì)設(shè)置子控件的Action為MotionEvent.ACTION_CANCEL汗盘,取消子控件的事件,并且注意以下代碼:

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

由于此時(shí)cancelChild=true, mFirstTouchTarget被設(shè)置成null,本次Move事件就結(jié)束了询一,注意ViewGroup是在下一Move事件才能夠接收到事件隐孽,因?yàn)橄乱淮蜯ove事件會(huì)重新走dispatchTouchEvent方法癌椿,關(guān)注以下代碼:

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

由于此時(shí)是Move事件并mFirstTouchTarget=null,所以此時(shí)走else分支intercepted = true菱阵,Move事件會(huì)交給自身處理踢俄,關(guān)聯(lián)代碼:

if (mFirstTouchTarget == null) {
    // No touch targets so treat this as an ordinary view.
    handled = dispatchTransformedTouchEvent(ev, canceled, null,
            TouchTarget.ALL_POINTER_IDS);
}

小結(jié)一下,當(dāng)父控件攔截Move事件時(shí)送粱,第一次會(huì)將子控件的事件類型設(shè)置為MotionEvent.ACTION_CANCEL并將mFirstTouchTarget賦值為null,此時(shí)第一次Move事件結(jié)束(由于子控件的dispatchTransformedTouchEvent返回true),第二次以后的Move事件才會(huì)傳遞到父控件褪贵。

UP與Cancel事件

一次完整的事件,首先有Down事件開始抗俄,中間有多個(gè)Move事件脆丁,最后由Up/Cancel事件結(jié)束,Up事件是正常結(jié)束动雹,而Cancel事件是被父控件攔截后產(chǎn)生的

事件分發(fā)完整流程圖

為了進(jìn)一步理解上述的源碼分析流程槽卫,下面提供一張完整的事件分發(fā)流程圖:

896629-20171007002836974-997068426.png

事件沖突處理

通過前面的鋪墊,可以知道事件沖突只能在Move事件中處理胰蝠,可以通過外部攔截和內(nèi)部攔截處理事件沖突歼培,這里以SwipeRefreshLayout嵌套ViewPager為例:

外部攔截

根據(jù)父控件的滑動(dòng)邏輯在onInterceptTouchEvent方法中返回true/false,核心代碼:

public class CustomSRL2 extends SwipeRefreshLayout {

    //外部攔截成員變量
    private float startX;
    private float startY;
    //ViewPager是否滾動(dòng)
    boolean mIsVpMove = false;
    //觸發(fā)移動(dòng)事件的最小距離,如果小于這個(gè)距離就不觸發(fā)移動(dòng)控件,如Viewpager就是用這個(gè)距離來判斷用戶是否翻頁
    private int mTouchSlop;

    public CustomSRL2(Context context) {
        this(context, null);
    }

    public CustomSRL2(Context context, AttributeSet attrs) {
        super(context, attrs);
        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
    }

    //外部攔截
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                startX = ev.getX();
                startY = ev.getY();
                mIsVpMove = false;
                break;
            case MotionEvent.ACTION_MOVE:
                //若此時(shí)ViewPager還在滑動(dòng)茸塞,則返回false,不攔截
                if (mIsVpMove) {
                    return false;
                }
                float x = ev.getX();
                float y = ev.getY();
                float deltaX = Math.abs(x - startX);
                float deltaY = Math.abs(y - startY);
                if (deltaX > mTouchSlop && deltaX > deltaY) {
                    mIsVpMove = true;
                    return false;
                }
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                mIsVpMove = false;
                break;
        }
        return super.onInterceptTouchEvent(ev);
    }
}

內(nèi)部攔截

根據(jù)子控件的滑動(dòng)邏輯調(diào)用父控的requestDisallowInterceptTouchEvent(true/false)方法通知父控件是否不攔截事件,核心代碼:

public class CustomSRL2 extends SwipeRefreshLayout {

    public CustomSRL2(Context context) {
        super(context);
    }

    public CustomSRL2(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    //以下代碼為內(nèi)部攔截代碼
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        //在ACTION_DOWN事件返回false,不攔截事件躲庄,將事件交給子控件處理
        if(ev.getAction() == MotionEvent.ACTION_DOWN){
            super.onInterceptTouchEvent(ev);
            return false;
        }
        return true;//攔截事件
    }
}

public class CustomVPInner extends ViewPager {

    private float startX;
    private float startY;

    public CustomVPInner(Context context) {
        super(context);
    }

    public CustomVPInner(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    //內(nèi)部攔截:使用ViewCompat.setNestedScrollingEnabled(this,true),參考以下代碼
    /**
     * public void requestDisallowInterceptTouchEvent(boolean b) {
     *         // if this is a List < L or another view that doesn't support nested
     *         // scrolling, ignore this request so that the vertical scroll event
     *         // isn't stolen
     *         if ((android.os.Build.VERSION.SDK_INT < 21 && mTarget instanceof AbsListView)
     *                 || (mTarget != null && !ViewCompat.isNestedScrollingEnabled(mTarget))) {
     *             // Nope.
     *         } else {
     *             super.requestDisallowInterceptTouchEvent(b);
     *         }
     *     }
     */
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                startX = ev.getX();
                startY = ev.getY();
                ViewCompat.setNestedScrollingEnabled(this,true);
                getParent().requestDisallowInterceptTouchEvent(true);
                break;
            case MotionEvent.ACTION_MOVE:
                float x = ev.getX();
                float y = ev.getY();
                float deltaX = Math.abs(x - startX);
                float deltaY = Math.abs(y - startY);
                if (deltaX < deltaY) {
                    getParent().requestDisallowInterceptTouchEvent(false);
                }
                break;
        }

        //打印ViewPager是否消費(fèi)了該事件钾虐,如果沒有噪窘,事件還是會(huì)交給SwipeRefreshLayout處理
        boolean consume = super.dispatchTouchEvent(ev);
        Log.e("fmt","consume=" + consume);

        return super.dispatchTouchEvent(ev);
    }
}

完整代碼實(shí)現(xiàn)

百度鏈接
密碼:1cq9

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市效扫,隨后出現(xiàn)的幾起案子倔监,更是在濱河造成了極大的恐慌,老刑警劉巖菌仁,帶你破解...
    沈念sama閱讀 216,997評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件浩习,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡济丘,警方通過查閱死者的電腦和手機(jī)谱秽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,603評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來摹迷,“玉大人弯院,你說我怎么就攤上這事±嵯疲” “怎么了听绳?”我有些...
    開封第一講書人閱讀 163,359評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長异赫。 經(jīng)常有香客問我椅挣,道長头岔,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,309評(píng)論 1 292
  • 正文 為了忘掉前任鼠证,我火速辦了婚禮峡竣,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘量九。我一直安慰自己适掰,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,346評(píng)論 6 390
  • 文/花漫 我一把揭開白布荠列。 她就那樣靜靜地躺著类浪,像睡著了一般。 火紅的嫁衣襯著肌膚如雪肌似。 梳的紋絲不亂的頭發(fā)上费就,一...
    開封第一講書人閱讀 51,258評(píng)論 1 300
  • 那天,我揣著相機(jī)與錄音川队,去河邊找鬼力细。 笑死,一個(gè)胖子當(dāng)著我的面吹牛固额,可吹牛的內(nèi)容都是我干的眠蚂。 我是一名探鬼主播,決...
    沈念sama閱讀 40,122評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼斗躏,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼逝慧!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起瑟捣,我...
    開封第一講書人閱讀 38,970評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎栅干,沒想到半個(gè)月后迈套,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,403評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡碱鳞,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,596評(píng)論 3 334
  • 正文 我和宋清朗相戀三年桑李,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片窿给。...
    茶點(diǎn)故事閱讀 39,769評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡贵白,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出崩泡,到底是詐尸還是另有隱情禁荒,我是刑警寧澤,帶...
    沈念sama閱讀 35,464評(píng)論 5 344
  • 正文 年R本政府宣布角撞,位于F島的核電站呛伴,受9級(jí)特大地震影響勃痴,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜热康,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,075評(píng)論 3 327
  • 文/蒙蒙 一沛申、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧姐军,春花似錦铁材、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,705評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至歇攻,卻和暖如春固惯,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背缴守。 一陣腳步聲響...
    開封第一講書人閱讀 32,848評(píng)論 1 269
  • 我被黑心中介騙來泰國打工葬毫, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人屡穗。 一個(gè)月前我還...
    沈念sama閱讀 47,831評(píng)論 2 370
  • 正文 我出身青樓贴捡,卻偏偏與公主長得像,于是被迫代替她去往敵國和親村砂。 傳聞我的和親對(duì)象是個(gè)殘疾皇子烂斋,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,678評(píng)論 2 354