Android NestedScrolling(嵌套滑動)機(jī)制

推薦:

http://www.reibang.com/p/aff5e82f0174
http://blog.csdn.net/lmj623565791/article/details/52204039#reply
搭配使用效果更佳

前言:

在android 5.0之前要是想要實(shí)現(xiàn)嵌套滑動,需要自己做對應(yīng)的事件處理:

dispatchTouchEvent()
onInterceptTouchEvent()
onTouchEvent()

但是在5.0之后google為我們提供了NestedScrolling機(jī)制擂找,包含如下四個(gè)方法(support.v4為大家提供了:NestedScrollingParent括授、NestedScrollingChild):

NestedScrollingChild
NestedScrollingParent
NestedScrollingChildHelper
NestedScrollingParentHelper

關(guān)于事件分發(fā)的介紹-part one:

  • Touch事件分發(fā)的主角只有兩個(gè):ViewGroup、View践瓷,Activity的touch事件實(shí)際上是調(diào)用內(nèi)部的ViewGroup的Touch事件电爹,可以直接當(dāng)成ViewGroup處理须鼎。

  • View在ViewGroup內(nèi)诞外,ViewGroup也可以在其他ViewGroup內(nèi),這時(shí)候把內(nèi)部的ViewGroup當(dāng)成View來分析灾票。

  • ViewGroup的相關(guān)事件有三個(gè):onInterceptTouchEvent峡谊、dispatchTouchEvent、onTouchEvent刊苍。View的相關(guān)事件只有兩個(gè):dispatchTouchEvent既们、onTouchEvent。Activity的相關(guān)事件有兩個(gè):dispatchTouchEvent正什、onTouchEvent啥纸。

先分析 ViewGroup的處理流程:首先得有個(gè)結(jié)構(gòu)模型概念:ViewGroup和View組成了一棵樹形結(jié)構(gòu),最頂層為Activity的 ViewGroup婴氮,下面有若干的ViewGroup節(jié)點(diǎn)斯棒,每個(gè)節(jié)點(diǎn)之下又有若干的ViewGroup節(jié)點(diǎn)或者View節(jié)點(diǎn),依次類推主经。如圖:

常見的典型touch事件結(jié)構(gòu)圖

當(dāng)一個(gè)Touch事件(觸摸事件)到達(dá)根結(jié)點(diǎn)荣暮,即activity的ViewGroup時(shí),它會依次下發(fā)罩驻,下發(fā)的過程調(diào)用子View或者Viewgroup的dispatchTouchEvent方法穗酥。簡單來說,就是ViewGroup會遍歷它包含的子View,調(diào)用每個(gè)View的dispatchTouchEvent,當(dāng)遇到ViewGroup時(shí),又會調(diào)用ViewGroup的dispatchTouchEvent方法砾跃,繼續(xù)調(diào)用其內(nèi)部的View的dispatchTouchEvent方法骏啰,上圖中的調(diào)用順序?yàn)椋?->2->5->6->7->3->4 dispatchTouchEvent方法只負(fù)責(zé)事件的分發(fā),它擁有boolean類型的返回值抽高,當(dāng)返回為true時(shí)判耕, 順序下發(fā)會中斷。在上述例子中如果5的dispatchTouchEvent返回結(jié)果為true厨内,那么6->7->3->4將都接收不到本次Touch事件祈秕。

關(guān)于ViewGroup中dispatchTouchEvent簡單分析:

/*** ViewGroup
 * @param ev
 * @return
 */
public boolean dispatchTouchEvent(MotionEvent ev){
    ....//其他處理,在此不管
    View[] views=getChildView();
    for(int i=0;i<views.length;i++){
      //判斷下Touch到屏幕上的點(diǎn)在該子View上面 
        if(...){
        if(views[i].dispatchTouchEvent(ev))
          return true;
         }
    }
    ...//其他處理雏胃,在此不管
}
/**     * View
 * @param ev
 * @return
 */
public boolean dispatchTouchEvent(MotionEvent ev){
    ....//其他處理请毛,在此不管
    return false;
}

由此可見,ViewGroup中的dispatchTouchEvent是真正執(zhí)行“分發(fā)”工作瞭亮,而View中對應(yīng)的dispatchTouchEvent并不執(zhí)行分發(fā)工作方仿,或者說它分發(fā)的對象就是他自己,決定是否把touch事件交給自己處理统翩,而處理的方法仙蚜,便是onTouchEvent事件,事實(shí)上子View的dispatchTouchEvent方法真正執(zhí)行的代碼是這樣的

/*** View
 * @param ev
 * @return
 */
public boolean dispatchTouchEvent(MotionEvent ev){
    ....//其他處理厂汗,在此不管
    return onTouchEvent(event);
}

一般情況下委粉,我們不該在普通View內(nèi)重寫dispatchTouchEvent方法,因?yàn)樗⒉粓?zhí)行分發(fā)邏輯娶桦。當(dāng)Touch事件到達(dá)View時(shí)贾节,我們該做的就是是否在onTouchEvent事件中處理它。

那么衷畦,ViewGroup的onTouchEvent事件是什么時(shí)候處理的呢栗涂?當(dāng)ViewGroup所有的子View都返回false時(shí),onTouchEvent事件便會執(zhí)行祈争。由于ViewGroup是繼承于View的斤程,它其實(shí)也是通過調(diào)用View的dispatchTouchEvent方法來執(zhí)行onTouchEvent事件。在目前的情況看來菩混,似乎只要我們把所有的onTouchEvent都返回false忿墅,就能保證所有的子控件都響應(yīng)本次Touch事件了。但必須要說明的是沮峡,這里的Touch事件球匕,只限于Acition_Down事件,即觸摸按下事件,而Aciton_UP和Action_MOVE卻不會執(zhí)行帖烘。事實(shí)上亮曹,一次完整的Touch事件橄杨,應(yīng)該是由一個(gè)Down、一個(gè)Up和若干個(gè)Move組成的照卦。Down方式通過dispatchTouchEvent分發(fā)式矫,分發(fā)的目的是為了找到真正需要處理完整Touch請求的View。當(dāng)某個(gè)View或者ViewGroup的onTouchEvent事件返回true時(shí)役耕,便表示它是真正要處理這次請求的View采转,之后的Aciton_UP和Action_MOVE將由它處理。當(dāng)所有子View的onTouchEvent都返回false時(shí)瞬痘,這次的Touch請求就由根ViewGroup故慈,即Activity自己處理了。

View mTarget=null;//保存捕獲Touch事件處理的View
public boolean dispatchTouchEvent(MotionEvent ev) {
    //....其他處理框全,在此不管
    if(ev.getAction()==KeyEvent.ACTION_DOWN){
        //每次Down事件察绷,都置為Null
       if(!onInterceptTouchEvent()){ 
        mTarget=null;
        View[] views=getChildView();
        for(int i=0;i<views.length;i++){
            if(views[i].dispatchTouchEvent(ev))
                mTarget=views[i];
                return true;
        }
      }

    }

    //當(dāng)子View沒有捕獲down事件時(shí),ViewGroup自身處理津辩。這里處理的Touch事件包含Down拆撼、 Up和Move

    if(mTarget==null){
        return super.dispatchTouchEvent(ev);
    }
    //...其他處理,在此不管
    if(onInterceptTouchEvent()){
    //...其他處理喘沿,在此不管   
    }
    //這一步在Action_Down中是不會執(zhí)行到的闸度,只有Move和UP才會執(zhí)行到。
    return mTarget.dispatchTouchEvent(ev);
}

ViewGroup還有個(gè)onInterceptTouchEvent蚜印,看名字便知道這是個(gè)攔截事件莺禁。這個(gè)攔截事件需要分兩種情況來說明:

1.假如我們在某個(gè)ViewGroup的onInterceptTouchEvent中,將Action為Down的Touch事件返回true窄赋,那便表示將該ViewGroup的所有下發(fā)操作攔截掉哟冬,這種情況下,mTarget會一直為null寝凌,因?yàn)閙Target是在Down事件中賦值的柒傻。由于mTarge為null孝赫,該ViewGroup的onTouchEvent事件被執(zhí)行较木。這種情況下可以把這個(gè)ViewGroup直接當(dāng)成View來對待。
2.假如我們在某個(gè)ViewGroup的onInterceptTouchEvent中青柄,將Acion為Down的Touch事件都返回false伐债,其他的都返回True,這種情況下致开,Down事件能正常分發(fā)峰锁,若子View都返回false,那mTarget還是為空双戳,無影響虹蒋。若某個(gè)子View返回了true,mTarget被賦值了,在Action_Move和Aciton_UP分發(fā)到該ViewGroup時(shí)魄衅,便會給mTarget分發(fā)一個(gè)Action_Delete的MotionEvent峭竣,同時(shí)清空mTarget的值,使得接下去的Action_Move(如果上一個(gè)操作不是UP)將由ViewGroup的onTouchEvent處理晃虫。情況一用到的比較多皆撩,情況二個(gè)人還未找到使用場景。

從頭到尾總結(jié)一下:

  • Touch事件分發(fā)中只有兩個(gè)主角:ViewGroup和View哲银。ViewGroup包含onInterceptTouchEvent扛吞、dispatchTouchEvent、onTouchEvent三個(gè)相關(guān)事件荆责。View包含dispatchTouchEvent滥比、onTouchEvent兩個(gè)相關(guān)事件。其中ViewGroup又繼承于View草巡。

  • ViewGroup和View組成了一個(gè)樹狀結(jié)構(gòu)守呜,根節(jié)點(diǎn)為Activity內(nèi)部包含的一個(gè)ViwGroup。

  • 觸摸事件由Action_Down山憨、Action_Move查乒、Aciton_UP組成,其中一次完整的觸摸事件中郁竟,Down和Up都只有一個(gè)玛迄,Move有若干個(gè),可以為0個(gè)棚亩。

  • 當(dāng)Acitivty接收到Touch事件時(shí)蓖议,將遍歷子View進(jìn)行Down事件的分發(fā)。ViewGroup的遍歷可以看成是遞歸的讥蟆。分發(fā)的目的是為了找到真正要處理本次完整觸摸事件的View勒虾,這個(gè)View會在onTouchuEvent結(jié)果返回true。

  • 當(dāng)某個(gè)子View返回true時(shí)瘸彤,會中止Down事件的分發(fā)修然,同時(shí)在ViewGroup中記錄該子View。接下去的Move和Up事件將由該子View直接進(jìn)行處理质况。由于子View是保存在ViewGroup中的愕宋,多層ViewGroup的節(jié)點(diǎn)結(jié)構(gòu)時(shí),上級ViewGroup保存的會是真實(shí)處理事件的View所在的ViewGroup對象:如ViewGroup0-ViewGroup1-TextView的結(jié)構(gòu)中结榄,TextView返回了true中贝,它將被保存在ViewGroup1中,而ViewGroup1也會返回true臼朗,被保存在ViewGroup0中邻寿。當(dāng)Move和UP事件來時(shí)蝎土,會先從ViewGroup0傳遞至ViewGroup1,再由ViewGroup1傳遞至TextView绣否。

  • 當(dāng)ViewGroup中所有子View都不捕獲Down事件時(shí)瘟则,將觸發(fā)ViewGroup自身的onTouch事件。觸發(fā)的方式是調(diào)用 super.dispatchTouchEvent函數(shù)枝秤,即父類View的dispatchTouchEvent方法醋拧。在所有子View都不處理的情況下,觸發(fā)Acitivity的onTouchEvent方法淀弹。

  • onInterceptTouchEvent有兩個(gè)作用:1.攔截Down事件的分發(fā)丹壕。2.中止Up和Move事件向目標(biāo)View傳遞,使得目標(biāo)View所在的ViewGroup捕獲Up和Move事件薇溃。

補(bǔ)充:“觸摸事件由Action_Down菌赖、Action_Move、Aciton_UP組成沐序,其中一次完整的觸摸事件中琉用,Down和Up都只有一個(gè),Move有若干個(gè)策幼,可以為0個(gè)邑时。”特姐,這里補(bǔ)充下其實(shí)UP事件是可能為0個(gè)的晶丘。

對于onInterceptTouchEvent事件,它的應(yīng)用場景在很多帶scroll效果的ViewGroup中都有體現(xiàn)唐含。設(shè)想一下再一個(gè)ViewPager中浅浮,每個(gè)Item都是個(gè)ImageView,我們需要對這些ImageView做Matrix操作捷枯,這不可避免要捕獲掉Touch事件滚秩,但是我們又需要做到不影響ViewPager翻頁效果,這又必須保證ViewPager能捕獲到Move事件淮捆,于是郁油,ViewPager的onInterceptTouchEvent會對Move事件做一個(gè)過濾,當(dāng)適當(dāng)條件的Move事件(持續(xù)若干事件或移動若干距離争剿,這里我沒讀源碼只是猜測)觸發(fā)時(shí)已艰,并會攔截掉痊末,返回子View一個(gè)Action_Cancel事件蚕苇。這個(gè)時(shí)候子View就沒有Up事件了,很多需要在Up中處理的事物要轉(zhuǎn)到Cancel中處理凿叠。

單個(gè)控件的事件分發(fā)流程圖如下:(以Button為例)

button 觸摸事件的處理

因此涩笤,事件分發(fā)之間的關(guān)系是:dispatchTouchEvent方法中先執(zhí)行 onTouch接口回調(diào)嚼吞,然后根據(jù)onTouch方法的返回值判斷是否執(zhí)行onTouchEvent方法,onTouchEvent方法中執(zhí)行了onClick接口回調(diào)蹬碧。
在ViewGroup中onInterceptTouchEvent返回true時(shí)舱禽,才會調(diào)用自己的onTouchEvent,dispatchTouchEvent返回true,不會調(diào)用自己的onTouchEvent。

結(jié)論:dispatchTouchEvent---->onTouch---->onTouchEvent----->onClick恩沽。并且如果仔細(xì)的你會發(fā)現(xiàn)誊稚,是在**所有ACTION_UP事件之后才觸發(fā)onClick點(diǎn)擊事件。 **

關(guān)于事件分發(fā)的介紹-part two:

Android中默認(rèn)情況下事件傳遞是由最終的view的接收到罗心,傳遞過程是從父布局到子布局里伯,也就是從Activity到ViewGroup到View的過程,默認(rèn)情況渤闷,ViewGroup起到的是透傳作用疾瓮。Android中事件傳遞過程(按箭頭方向)如下圖:

觸摸事件整體分析

觸摸事件是一連串ACTION_DOWN,ACTION_MOVE..MOVE…MOVE飒箭、最后ACTION_UP狼电,觸摸事件還有ACTION_CANCEL事件。事件都是從ACTION_DOWN開始的弦蹂,Activity的dispatchTouchEvent()首先接收到ACTION_DOWN肩碟,執(zhí)行super.dispatchTouchEvent(ev),事件向下分發(fā)凸椿。
dispatchTouchEvent()返回true腾务,后續(xù)事件(ACTION_MOVE、ACTION_UP)會再傳遞削饵,如果返回false岩瘦,dispatchTouchEvent()就接收不到ACTION_UP、ACTION_MOVE窿撬。

ACTION_DOWN都沒被消費(fèi)
ACTION_DOWN被View消費(fèi)了
后續(xù)ACTION_MOVE和UP在不被攔截的情況下都會去找VIEW
后續(xù)的被攔截了
ACTION_DOWN一開始就被攔截

android中的Touch事件都是從ACTION_DOWN開始的:

**單手指操作:ACTION_DOWN---ACTION_MOVE----ACTION_UP
多手指操作:ACTION_DOWN---ACTION_POINTER_DOWN---ACTION_MOVE--ACTION_POINTER_UP---ACTION_UP
**

關(guān)于嵌套滑動機(jī)制:

/**
 * 重點(diǎn)關(guān)注方法:
 * onStartNestedScroll
 * onNestedPreScroll
 */
public class StickyNavLayout extends LinearLayout implements NestedScrollingParent {
    private static final String TAG = "StickyNavLayout";

    /**
     * onStartNestedScroll該方法返回true启昧,代表當(dāng)前ViewGroup能接受內(nèi)部View的滑動參數(shù)(這個(gè)內(nèi)部View不一定是直接子View),
     * 一般情況下建議直接返回true劈伴,當(dāng)然你可以根據(jù)nestedScrollAxes:判斷垂直或水平方向才返回true密末。
     */
    @Override
    public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
        Log.e(TAG, "onStartNestedScroll");
        return true;
    }

    @Override
    public void onNestedScrollAccepted(View child, View target, int nestedScrollAxes) {
        Log.e(TAG, "onNestedScrollAccepted");
    }

    @Override
    public void onStopNestedScroll(View target) {
        Log.e(TAG, "onStopNestedScroll");
    }

    @Override
    public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
        Log.e(TAG, "onNestedScroll");
    }

    @Override
    public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
        Log.e(TAG, "onNestedPreScroll");
        boolean hiddenTop = dy > 0 && getScrollY() < mTopViewHeight;
        boolean showTop = dy < 0 && getScrollY() >= 0 && !ViewCompat.canScrollVertically(target, -1);
        Log.d("onNestedPreScroll", "--------------------------------------------------------------------------------");
        Log.d("onNestedPreScroll", "ViewCompat.canScrollVertically(target, -1) target view 向下滾動--->:" + ViewCompat.canScrollVertically(target, -1));
        Log.d("onNestedPreScroll", "!ViewCompat.canScrollVertically(target, -1) --->:" + !ViewCompat.canScrollVertically(target, -1));
        Log.d("onNestedPreScroll", "ViewCompat.canScrollVertically(target, 1) target  view 向上滾動--->:" + ViewCompat.canScrollVertically(target, 1));
        Log.d("onNestedPreScroll", "!ViewCompat.canScrollVertically(target, 1)--->:" + !ViewCompat.canScrollVertically(target, 1));
        Log.d("onNestedPreScroll", "hiddenTop ->" + String.valueOf(hiddenTop));
        Log.d("onNestedPreScroll", "showTop ->" + String.valueOf(showTop));
        Log.d("onNestedPreScroll", "dy/2 ->" + String.valueOf(dy / 2));
        Log.d("onNestedPreScroll", "--------------------------------------------------------------------------------");
        if (hiddenTop || showTop) {
            /**
             * 這個(gè)是top 每次scroll的距離
             */
            scrollBy(0, dy);
            /**
             * 這個(gè)是top消耗的距離
             * 如果為dy/2,則余下的dy/2由子view消耗,即滑動的過程中跛璧,子view同時(shí)也會滑動
             */
            consumed[1] = dy;
        }
    }

    @Override
    public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {
        Log.e(TAG, "onNestedFling");
        return false;
    }

    @Override
    public boolean onNestedPreFling(View target, float velocityX, float velocityY) {
        Log.e(TAG, "onNestedPreFling");
        //down - //up+
        if (getScrollY() >= mTopViewHeight) return false;
        fling((int) velocityY);
        return true;
    }

    @Override
    public int getNestedScrollAxes() {
        Log.e(TAG, "getNestedScrollAxes");
        return 0;
    }

    private View mTop;
    private View mNav;
    private ViewPager mViewPager;

    private int mTopViewHeight;

    private OverScroller mScroller;
    private VelocityTracker mVelocityTracker;
    private int mTouchSlop;
    private int mMaximumVelocity, mMinimumVelocity;

    private float mLastY;
    private boolean mDragging;

    public StickyNavLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        setOrientation(LinearLayout.VERTICAL);

        mScroller = new OverScroller(context);
        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
        mMaximumVelocity = ViewConfiguration.get(context)
                .getScaledMaximumFlingVelocity();
        mMinimumVelocity = ViewConfiguration.get(context)
                .getScaledMinimumFlingVelocity();

    }

    private void initVelocityTrackerIfNotExists() {
        if (mVelocityTracker == null) {
            mVelocityTracker = VelocityTracker.obtain();
        }
    }

    private void recycleVelocityTracker() {
        if (mVelocityTracker != null) {
            mVelocityTracker.recycle();
            mVelocityTracker = null;
        }
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        mTop = findViewById(R.id.id_stickynavlayout_topview);
        mNav = findViewById(R.id.id_stickynavlayout_indicator);
        View view = findViewById(R.id.id_stickynavlayout_viewpager);
        if (!(view instanceof ViewPager)) {
            throw new RuntimeException(
                    "id_stickynavlayout_viewpager show used by ViewPager !");
        }
        mViewPager = (ViewPager) view;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        //不限制頂部的高度
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        getChildAt(0).measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
        ViewGroup.LayoutParams params = mViewPager.getLayoutParams();
        params.height = getMeasuredHeight() - mNav.getMeasuredHeight();
        setMeasuredDimension(getMeasuredWidth(), mTop.getMeasuredHeight() + mNav.getMeasuredHeight() + mViewPager.getMeasuredHeight());

    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mTopViewHeight = mTop.getMeasuredHeight();
    }

    /**
     * 對于fling方法严里,我們使用OverScroller的fling方法,另外邊界檢測追城,重寫了scrollTo方法:
     */
    public void fling(int velocityY) {
        mScroller.fling(0, getScrollY(), 0, velocityY, 0, 0, 0, mTopViewHeight);
        invalidate();   
    }

    @Override
    public void scrollTo(int x, int y) {
        if (y < 0) {
            y = 0;
        }
        if (y > mTopViewHeight) {
            y = mTopViewHeight;
        }
        if (y != getScrollY()) {
            super.scrollTo(x, y);
        }
    }

    @Override
    public void computeScroll() {
        if (mScroller.computeScrollOffset()) {
            scrollTo(0, mScroller.getCurrY());
            invalidate();
        }
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末刹碾,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子座柱,更是在濱河造成了極大的恐慌迷帜,老刑警劉巖物舒,帶你破解...
    沈念sama閱讀 222,104評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異戏锹,居然都是意外死亡冠胯,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評論 3 399
  • 文/潘曉璐 我一進(jìn)店門锦针,熙熙樓的掌柜王于貴愁眉苦臉地迎上來荠察,“玉大人,你說我怎么就攤上這事奈搜「盍福” “怎么了?”我有些...
    開封第一講書人閱讀 168,697評論 0 360
  • 文/不壞的土叔 我叫張陵媚污,是天一觀的道長舀瓢。 經(jīng)常有香客問我,道長耗美,這世上最難降的妖魔是什么京髓? 我笑而不...
    開封第一講書人閱讀 59,836評論 1 298
  • 正文 為了忘掉前任,我火速辦了婚禮商架,結(jié)果婚禮上堰怨,老公的妹妹穿的比我還像新娘。我一直安慰自己蛇摸,他們只是感情好备图,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,851評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著赶袄,像睡著了一般揽涮。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上饿肺,一...
    開封第一講書人閱讀 52,441評論 1 310
  • 那天蒋困,我揣著相機(jī)與錄音,去河邊找鬼敬辣。 笑死雪标,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的溉跃。 我是一名探鬼主播村刨,決...
    沈念sama閱讀 40,992評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼撰茎!你這毒婦竟也來了嵌牺?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,899評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎髓梅,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體绎签,經(jīng)...
    沈念sama閱讀 46,457評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡枯饿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,529評論 3 341
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了诡必。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片奢方。...
    茶點(diǎn)故事閱讀 40,664評論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖爸舒,靈堂內(nèi)的尸體忽然破棺而出蟋字,到底是詐尸還是另有隱情,我是刑警寧澤扭勉,帶...
    沈念sama閱讀 36,346評論 5 350
  • 正文 年R本政府宣布鹊奖,位于F島的核電站,受9級特大地震影響涂炎,放射性物質(zhì)發(fā)生泄漏忠聚。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,025評論 3 334
  • 文/蒙蒙 一唱捣、第九天 我趴在偏房一處隱蔽的房頂上張望两蟀。 院中可真熱鬧,春花似錦震缭、人聲如沸赂毯。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽党涕。三九已至,卻和暖如春巡社,著一層夾襖步出監(jiān)牢的瞬間遣鼓,已是汗流浹背萍嬉。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評論 1 272
  • 我被黑心中介騙來泰國打工癣漆, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人态贤。 一個(gè)月前我還...
    沈念sama閱讀 49,081評論 3 377
  • 正文 我出身青樓气笙,卻偏偏與公主長得像次企,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子潜圃,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,675評論 2 359

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