Android觸摸事件的應(yīng)用

前言

上一篇講了Android觸摸事件的傳遞機(jī)制敷燎,具體可以看這里 初識Android觸摸事件傳遞機(jī)制暂筝。既然知道Android中觸摸事件的傳遞分發(fā)响鹃,那么它能解決什么樣的問題,在我們實(shí)際開發(fā)中如何應(yīng)用淑仆,這點(diǎn)很重要荸百,知道原理是為了解決問題而準(zhǔn)備的。這篇文章的核心講的如何解決View的滑動(dòng)沖突党觅,這個(gè)問題在日常開發(fā)中很常見肌厨,比如內(nèi)部嵌套Fragment視圖是左右滑動(dòng)滚婉,外部用一個(gè)ScrollView來包含墨状,可以上下滑動(dòng)卫漫,如果不進(jìn)行滑動(dòng)沖突處理的話,就會造成外部滑動(dòng)方向和內(nèi)部滑動(dòng)方向不一致肾砂。

目錄

  • 常見的滑動(dòng)沖突場景
  • 滑動(dòng)沖突的處理規(guī)則
  • 外部攔截法
  • 內(nèi)部攔截法
  • 小結(jié)

常見的滑動(dòng)沖突場景

常見的滑動(dòng)沖突場景可以簡單分為以下三種:

  • 場景1:外部滑動(dòng)方向和內(nèi)部滑動(dòng)方向不一致
  • 場景2:外部滑動(dòng)方向和內(nèi)部滑動(dòng)方向一致
  • 場景3:上面兩種情況的嵌套

如圖:

滑動(dòng)沖突場景

場景1列赎,主要是將ViewPager和Fragment配合使用所組成的頁面滑動(dòng)效果,主流應(yīng)用幾乎都會使用這個(gè)效果镐确。在這個(gè)效果中可以通過左右滑動(dòng)來切換頁面包吝,而每個(gè)頁面內(nèi)部往往又是一個(gè)ListView,所以就造成了滑動(dòng)沖突源葫,但是在ViewPager內(nèi)部處理了這種滑動(dòng)沖突诗越,因此在采用ViewPager時(shí)我們就無須關(guān)注這個(gè)問題,而如果把ViewPager換成ScrollView息堂,那就必須自己手動(dòng)處理嚷狞,不然造成的結(jié)果就是內(nèi)外兩層只能一層能夠滑動(dòng)。

場景2荣堰,就復(fù)雜一點(diǎn)床未,當(dāng)內(nèi)外兩層都在同一個(gè)方向可以滑動(dòng)的時(shí)候,顯然存在邏輯問題振坚。因?yàn)楫?dāng)手指開始滑動(dòng)的時(shí)候薇搁,系統(tǒng)無法知道用戶到底是想讓哪一層滑動(dòng),所以當(dāng)手指滑動(dòng)的時(shí)候就會出現(xiàn)問題渡八,要么只有一層滑動(dòng)啃洋,要么就是內(nèi)外兩層都滑動(dòng)但很卡頓。

場景3呀狼,是場景1和場景2兩種情況的嵌套裂允,顯得更復(fù)雜了。比如外部有一個(gè)SlideMenu效果哥艇,內(nèi)部有一個(gè)ViewPager绝编,ViewPager的每一個(gè)頁面中又是一個(gè)ListView。雖然場景3滑動(dòng)沖突看起來很復(fù)雜貌踏,但都是幾個(gè)單一的滑動(dòng)沖突的疊加十饥,因此需要一一拆解開來即可。

滑動(dòng)沖突的處理規(guī)則

一般來說祖乳,不管滑動(dòng)沖突有多么復(fù)雜逗堵,它都有既定的規(guī)則,根據(jù)這些規(guī)則我們就可以選擇合適的方法去處理眷昆。

對于場景1蜒秤,它的處理規(guī)則就是:當(dāng)用戶左右滑動(dòng)時(shí)汁咏,需要讓外部的View攔截點(diǎn)擊事件,當(dāng)用戶上下滑動(dòng)作媚,需要讓內(nèi)部View攔截點(diǎn)擊事件攘滩。具體來說就是根據(jù)滑動(dòng)是水平滑動(dòng)還是豎直滑動(dòng)來判斷到底是由誰來攔截事件。

如圖:

滑動(dòng)過程圖

簡單來說纸泡,就是根據(jù)水平方向和豎直方向的距離差來判斷漂问,如果是Dx>Dy,那么則是水平滑動(dòng)女揭,如果是Dy>Dx蚤假,那么則是豎直滑動(dòng)。

場景2吧兔,則是比較特殊磷仰,它無法根據(jù)滑動(dòng)的角度,距離差以及速度差來做判斷掩驱。這個(gè)時(shí)候就需要從業(yè)務(wù)上找到突破點(diǎn)芒划,比如,當(dāng)處于某種狀態(tài)時(shí)需要外部View響應(yīng)用戶的滑動(dòng)欧穴,而處于另外一種狀態(tài)時(shí)需要內(nèi)部View來響應(yīng)View的滑動(dòng)

對于場景3的話民逼,它的滑動(dòng)規(guī)則也更復(fù)雜,和場景2一樣涮帘,同樣是從業(yè)務(wù)上找到突破點(diǎn)拼苍。

外部攔截法

外部攔截法是指點(diǎn)擊事件都是先經(jīng)過父容器的攔截處理,如果父容器需要此事件就攔截调缨,如果不需要此事件疮鲫,就不攔截了,這樣就可以解決滑動(dòng)沖突的問題弦叶,外部攔截法需要重寫父容器的onInterceptTouchEvent方法俊犯,在內(nèi)部做相應(yīng)的攔截即可,偽代碼如下:

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        boolean intercepted = false;
        int x = (int) event.getX();
        int y = (int) event.getY();

        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN: {
            intercepted = false;
            break;
        }
        case MotionEvent.ACTION_MOVE: {
            if (父容器需要點(diǎn)擊當(dāng)前事件) {
                intercepted = true;
            } else {
                intercepted = false;
            }
            break;
        }
        case MotionEvent.ACTION_UP: {
            intercepted = false;
            break;
        }
        default:
            break;
        }
        mLastXIntercept = x;
        mLastYIntercept = y;

        return intercepted;
    }

首先ACTION_DOWN這個(gè)事件伤哺,父容器必須返回false燕侠,這樣保證后續(xù)move和up的事件可以傳遞給子View,根據(jù)move事件來決定是否攔截立莉,如果父容器攔截就返回true绢彤,否則返回false。

實(shí)現(xiàn)一個(gè)自定義類似ViewPager的控件蜓耻,嵌套ListView的效果茫舶,源代碼如下:

public class HorizontalScrollViewEx extends ViewGroup {
    private static final String TAG = "HorizontalScrollViewEx";

    private int mChildrenSize;
    private int mChildWidth;
    private int mChildIndex;

    // 分別記錄上次滑動(dòng)的坐標(biāo)
    private int mLastX = 0;
    private int mLastY = 0;
    // 分別記錄上次滑動(dòng)的坐標(biāo)(onInterceptTouchEvent)
    private int mLastXIntercept = 0;
    private int mLastYIntercept = 0;

    private Scroller mScroller;                //彈性滑動(dòng)對象
    private VelocityTracker mVelocityTracker;  //追蹤滑動(dòng)速度

    public HorizontalScrollViewEx(Context context) {
        super(context);
        init();
    }

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

    public HorizontalScrollViewEx(Context context, AttributeSet attrs,
            int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

    private void init() {
        mScroller = new Scroller(getContext());
        mVelocityTracker = VelocityTracker.obtain();
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        boolean intercepted = false;
        int x = (int) event.getX();
        int y = (int) event.getY();

        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN: {
            intercepted = false;
            if (!mScroller.isFinished()) {
                mScroller.abortAnimation();
                intercepted = true;
            }
            break;
        }
        case MotionEvent.ACTION_MOVE: {
            int deltaX = x - mLastXIntercept;
            int deltaY = y - mLastYIntercept;
            if (Math.abs(deltaX) > Math.abs(deltaY)) {
                intercepted = true;
            } else {
                intercepted = false;
            }
            break;
        }
        case MotionEvent.ACTION_UP: {
            intercepted = false;
            break;
        }
        default:
            break;
        }

        Log.d(TAG, "intercepted=" + intercepted);
        mLastX = x;
        mLastY = y;
        mLastXIntercept = x;
        mLastYIntercept = y;

        return intercepted;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        mVelocityTracker.addMovement(event);
        int x = (int) event.getX();
        int y = (int) event.getY();
        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN: {
            if (!mScroller.isFinished()) {
                mScroller.abortAnimation();
            }
            break;
        }
        case MotionEvent.ACTION_MOVE: {
            int deltaX = x - mLastX;
            scrollBy(-deltaX, 0);
            break;
        }
        case MotionEvent.ACTION_UP: {
            int scrollX = getScrollX();
            mVelocityTracker.computeCurrentVelocity(1000);
            float xVelocity = mVelocityTracker.getXVelocity();
            if (Math.abs(xVelocity) >= 50) {
                mChildIndex = xVelocity > 0 ? mChildIndex - 1 : mChildIndex + 1;
            } else {
                mChildIndex = (scrollX + mChildWidth / 2) / mChildWidth;
            }
            mChildIndex = Math.max(0, Math.min(mChildIndex, mChildrenSize - 1));
            int dx = mChildIndex * mChildWidth - scrollX;
            smoothScrollBy(dx, 0);
            mVelocityTracker.clear();
            break;
        }
        default:
            break;
        }

        mLastX = x;
        mLastY = y;
        return true;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int measuredWidth = 0;
        int measuredHeight = 0;
        final int childCount = getChildCount();
        measureChildren(widthMeasureSpec, heightMeasureSpec);

        int widthSpaceSize = MeasureSpec.getSize(widthMeasureSpec);
        int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightSpaceSize = MeasureSpec.getSize(heightMeasureSpec);
        int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
        if (childCount == 0) {
            setMeasuredDimension(0, 0);
        } else if (heightSpecMode == MeasureSpec.AT_MOST) {
            final View childView = getChildAt(0);
            measuredHeight = childView.getMeasuredHeight();
            setMeasuredDimension(widthSpaceSize, childView.getMeasuredHeight());
        } else if (widthSpecMode == MeasureSpec.AT_MOST) {
            final View childView = getChildAt(0);
            measuredWidth = childView.getMeasuredWidth() * childCount;
            setMeasuredDimension(measuredWidth, heightSpaceSize);
        } else {
            final View childView = getChildAt(0);
            measuredWidth = childView.getMeasuredWidth() * childCount;
            measuredHeight = childView.getMeasuredHeight();
            setMeasuredDimension(measuredWidth, measuredHeight);
        }
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int childLeft = 0;
        final int childCount = getChildCount();
        mChildrenSize = childCount;

        for (int i = 0; i < childCount; i++) {
            final View childView = getChildAt(i);
            if (childView.getVisibility() != View.GONE) {
                final int childWidth = childView.getMeasuredWidth();
                mChildWidth = childWidth;
                childView.layout(childLeft, 0, childLeft + childWidth,
                        childView.getMeasuredHeight());
                childLeft += childWidth;
            }
        }
    }

    private void smoothScrollBy(int dx, int dy) {
        mScroller.startScroll(getScrollX(), 0, dx, 0, 500);
        invalidate();
    }

    @Override
    public void computeScroll() {
        if (mScroller.computeScrollOffset()) {
            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
            postInvalidate();
        }
    }

    @Override
    protected void onDetachedFromWindow() {
        mVelocityTracker.recycle();
        super.onDetachedFromWindow();
    }
}

這個(gè)情況的攔截條件就是父容器在滑動(dòng)過程中水平距離差比垂直距離差大,那么就進(jìn)行攔截刹淌,否則就不攔截饶氏,繼續(xù)傳遞事件讥耗。

內(nèi)部攔截法

內(nèi)部攔截法是指父容器不攔截任何事件,所有的事件都傳遞給子元素嚷往,如果子元素需要此事件就直接消耗掉葛账,否則就交給父容器進(jìn)行處理柠衅,這種方法和Android中的事件分發(fā)機(jī)制不一致皮仁,需要配合requestDisallowInterceptTouchEvent方法才能正常工作,使用起來較外部攔截法復(fù)雜菲宴。偽代碼如下:

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        int x = (int) event.getX();
        int y = (int) event.getY();

        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN: {
            mHorizontalScrollViewEx2.requestDisallowInterceptTouchEvent(true);
            break;
        }
        case MotionEvent.ACTION_MOVE: {
            int deltaX = x - mLastX;
            int deltaY = y - mLastY;
            if (父容器需要此類點(diǎn)擊事件) {
                mHorizontalScrollViewEx2.requestDisallowInterceptTouchEvent(false);
            }
            break;
        }
        case MotionEvent.ACTION_UP: {
            break;
        }
        default:
            break;
        }

        mLastX = x;
        mLastY = y;
        return super.dispatchTouchEvent(event);
    }

當(dāng)子元素調(diào)用requestDisallowInterceptTouchEvent(false)方法時(shí)贷祈,父元素才能繼續(xù)攔截所需的事件。

前面是用自定義類似的ViewPager喝峦,現(xiàn)在重寫一個(gè)ListView势誊,我們可以自定義一個(gè)ListView,叫做ListViewEx谣蠢,然后對內(nèi)部攔截法的模板代碼進(jìn)行修改即可粟耻。

public class ListViewEx extends ListView {
    private static final String TAG = "ListViewEx";

    private HorizontalScrollViewEx2 mHorizontalScrollViewEx2;

    // 分別記錄上次滑動(dòng)的坐標(biāo)
    private int mLastX = 0;
    private int mLastY = 0;

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

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

    public ListViewEx(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    public void setHorizontalScrollViewEx2(
            HorizontalScrollViewEx2 horizontalScrollViewEx2) {
        mHorizontalScrollViewEx2 = horizontalScrollViewEx2;
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        int x = (int) event.getX();
        int y = (int) event.getY();

        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN: {
            mHorizontalScrollViewEx2.requestDisallowInterceptTouchEvent(true);
            break;
        }
        case MotionEvent.ACTION_MOVE: {
            int deltaX = x - mLastX;
            int deltaY = y - mLastY;
            Log.d(TAG, "dx:" + deltaX + " dy:" + deltaY);
            if (Math.abs(deltaX) > Math.abs(deltaY)) {
                mHorizontalScrollViewEx2.requestDisallowInterceptTouchEvent(false);
            }
            break;
        }
        case MotionEvent.ACTION_UP: {
            break;
        }
        default:
            break;
        }

        mLastX = x;
        mLastY = y;
        return super.dispatchTouchEvent(event);
    }
}

同時(shí)對于包含ListViewEx外部布局進(jìn)行修改,在onInterceptTouchEvent事件上不進(jìn)行攔截

public class HorizontalScrollViewEx2 extends ViewGroup {
    private static final String TAG = "HorizontalScrollViewEx2";

    private int mChildrenSize;
    private int mChildWidth;
    private int mChildIndex;
    // 分別記錄上次滑動(dòng)的坐標(biāo)
    private int mLastX = 0;
    private int mLastY = 0;

    // 分別記錄上次滑動(dòng)的坐標(biāo)(onInterceptTouchEvent)
    private int mLastXIntercept = 0;
    private int mLastYIntercept = 0;

    private Scroller mScroller;
    private VelocityTracker mVelocityTracker;

    public HorizontalScrollViewEx2(Context context) {
        super(context);
        init();
    }

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

    public HorizontalScrollViewEx2(Context context, AttributeSet attrs,
            int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

    private void init() {
        mScroller = new Scroller(getContext());
        mVelocityTracker = VelocityTracker.obtain();
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        int x = (int) event.getX();
        int y = (int) event.getY();
        int action = event.getAction();
        if (action == MotionEvent.ACTION_DOWN) {
            mLastX = x;
            mLastY = y;
            if (!mScroller.isFinished()) {
                mScroller.abortAnimation();
                return true;
            }
            return false;
        } else {
            return true;
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.d(TAG, "onTouchEvent action:" + event.getAction());
        mVelocityTracker.addMovement(event);
        int x = (int) event.getX();
        int y = (int) event.getY();
        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN: {
            if (!mScroller.isFinished()) {
                mScroller.abortAnimation();
            }
            break;
        }
        case MotionEvent.ACTION_MOVE: {
            int deltaX = x - mLastX;
            int deltaY = y - mLastY;
            Log.d(TAG, "move, deltaX:" + deltaX + " deltaY:" + deltaY);
            scrollBy(-deltaX, 0);
            break;
        }
        case MotionEvent.ACTION_UP: {
            int scrollX = getScrollX();
            int scrollToChildIndex = scrollX / mChildWidth;
            Log.d(TAG, "current index:" + scrollToChildIndex);
            mVelocityTracker.computeCurrentVelocity(1000);
            float xVelocity = mVelocityTracker.getXVelocity();
            if (Math.abs(xVelocity) >= 50) {
                mChildIndex = xVelocity > 0 ? mChildIndex - 1 : mChildIndex + 1;
            } else {
                mChildIndex = (scrollX + mChildWidth / 2) / mChildWidth;
            }
            mChildIndex = Math.max(0, Math.min(mChildIndex, mChildrenSize - 1));
            int dx = mChildIndex * mChildWidth - scrollX;
            smoothScrollBy(dx, 0);
            mVelocityTracker.clear();
            Log.d(TAG, "index:" + scrollToChildIndex + " dx:" + dx);
            break;
        }
        default:
            break;
        }

        mLastX = x;
        mLastY = y;
        return true;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int measuredWidth = 0;
        int measuredHeight = 0;
        final int childCount = getChildCount();
        measureChildren(widthMeasureSpec, heightMeasureSpec);

        int widthSpaceSize = MeasureSpec.getSize(widthMeasureSpec);
        int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightSpaceSize = MeasureSpec.getSize(heightMeasureSpec);
        int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
        if (childCount == 0) {
            setMeasuredDimension(0, 0);
        } else if (heightSpecMode == MeasureSpec.AT_MOST) {
            final View childView = getChildAt(0);
            measuredHeight = childView.getMeasuredHeight();
            setMeasuredDimension(widthSpaceSize, childView.getMeasuredHeight());
        } else if (widthSpecMode == MeasureSpec.AT_MOST) {
            final View childView = getChildAt(0);
            measuredWidth = childView.getMeasuredWidth() * childCount;
            setMeasuredDimension(measuredWidth, heightSpaceSize);
        } else {
            final View childView = getChildAt(0);
            measuredWidth = childView.getMeasuredWidth() * childCount;
            measuredHeight = childView.getMeasuredHeight();
            setMeasuredDimension(measuredWidth, measuredHeight);
        }
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        Log.d(TAG, "width:" + getWidth());
        int childLeft = 0;
        final int childCount = getChildCount();
        mChildrenSize = childCount;

        for (int i = 0; i < childCount; i++) {
            final View childView = getChildAt(i);
            if (childView.getVisibility() != View.GONE) {
                final int childWidth = childView.getMeasuredWidth();
                mChildWidth = childWidth;
                childView.layout(childLeft, 0, childLeft + childWidth,
                        childView.getMeasuredHeight());
                childLeft += childWidth;
            }
        }
    }

    private void smoothScrollBy(int dx, int dy) {
        mScroller.startScroll(getScrollX(), 0, dx, 0, 500);
        invalidate();
    }

    @Override
    public void computeScroll() {
        if (mScroller.computeScrollOffset()) {
            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
            postInvalidate();
        }
    }

    @Override
    protected void onDetachedFromWindow() {
        mVelocityTracker.recycle();
        super.onDetachedFromWindow();
    }
}

這個(gè)攔截規(guī)則也是父容器在滑動(dòng)過程中水平距離差與垂直距離差相比眉踱。

小結(jié)

總的來說挤忙,滑動(dòng)沖突的場景可以分為三種,內(nèi)外部方向不一致谈喳、內(nèi)外部方向一致册烈、嵌套前面兩種情況。如何解決婿禽,不管多么復(fù)雜的滑動(dòng)沖突赏僧,可以進(jìn)行拆分,根據(jù)的一定的規(guī)則扭倾,第一種情況可根據(jù)滑動(dòng)距離差淀零、速度差和角度差來解決,第二種和第三種情況膛壹,可根據(jù)業(yè)務(wù)上找到突破點(diǎn)驾中,業(yè)務(wù)上一種狀態(tài)需要響應(yīng),切換到另外一種狀態(tài)時(shí)則不響應(yīng)恢筝,根據(jù)業(yè)務(wù)需求得出相應(yīng)的處理規(guī)則哀卫,有了處理規(guī)則可以進(jìn)行下一步處理。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末撬槽,一起剝皮案震驚了整個(gè)濱河市此改,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌侄柔,老刑警劉巖共啃,帶你破解...
    沈念sama閱讀 211,265評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件占调,死亡現(xiàn)場離奇詭異,居然都是意外死亡移剪,警方通過查閱死者的電腦和手機(jī)究珊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來纵苛,“玉大人剿涮,你說我怎么就攤上這事」ト耍” “怎么了取试?”我有些...
    開封第一講書人閱讀 156,852評論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長怀吻。 經(jīng)常有香客問我瞬浓,道長,這世上最難降的妖魔是什么蓬坡? 我笑而不...
    開封第一講書人閱讀 56,408評論 1 283
  • 正文 為了忘掉前任猿棉,我火速辦了婚禮,結(jié)果婚禮上屑咳,老公的妹妹穿的比我還像新娘萨赁。我一直安慰自己,他們只是感情好乔宿,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評論 5 384
  • 文/花漫 我一把揭開白布位迂。 她就那樣靜靜地躺著,像睡著了一般详瑞。 火紅的嫁衣襯著肌膚如雪掂林。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,772評論 1 290
  • 那天坝橡,我揣著相機(jī)與錄音泻帮,去河邊找鬼。 笑死计寇,一個(gè)胖子當(dāng)著我的面吹牛锣杂,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播番宁,決...
    沈念sama閱讀 38,921評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼元莫,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了蝶押?” 一聲冷哼從身側(cè)響起踱蠢,我...
    開封第一講書人閱讀 37,688評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎棋电,沒想到半個(gè)月后茎截,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體苇侵,經(jīng)...
    沈念sama閱讀 44,130評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評論 2 325
  • 正文 我和宋清朗相戀三年企锌,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了榆浓。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,617評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡撕攒,死狀恐怖陡鹃,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情打却,我是刑警寧澤杉适,帶...
    沈念sama閱讀 34,276評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站柳击,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏片习。R本人自食惡果不足惜捌肴,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望藕咏。 院中可真熱鬧状知,春花似錦、人聲如沸孽查。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽盲再。三九已至西设,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間答朋,已是汗流浹背贷揽。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留梦碗,地道東北人禽绪。 一個(gè)月前我還...
    沈念sama閱讀 46,315評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像洪规,于是被迫代替她去往敵國和親印屁。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評論 2 348

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