一點(diǎn)見解: Android嵌套滑動(dòng)和NestedScrollView

問題分析

嵌套滑動(dòng)一直是Android中比較棘手的問題, 根本原因是Android的事件分發(fā)機(jī)制導(dǎo)致的.不了解事件分發(fā)機(jī)制的同學(xué)可以先看看一點(diǎn)見解: Android事件分發(fā)機(jī)制, 導(dǎo)致嵌套滑動(dòng)難處理的關(guān)鍵原因在于當(dāng)子控件消費(fèi)了事件, 那么父控件就不會(huì)再有機(jī)會(huì)處理這個(gè)事件了, 所以一旦內(nèi)部的滑動(dòng)控件消費(fèi)了滑動(dòng)操作, 外部的滑動(dòng)控件就再也沒機(jī)會(huì)響應(yīng)這個(gè)滑動(dòng)操作了.

嵌套滑動(dòng)

不過這個(gè)問題終于在LOLLIPOP(SDK21)之后終于有了官方的解決方法, 就是嵌套滑動(dòng)機(jī)制. 在分析具體的代碼邏輯之前, 下面先簡單介紹下嵌套滑動(dòng)的一些基本知識(shí).
嵌套滑動(dòng)機(jī)制可以理解為一個(gè)約定, 原生的支持嵌套滑動(dòng)的控件都是依據(jù)這個(gè)約定來實(shí)現(xiàn)嵌套滑動(dòng)的, 例如CoordinatorLayout, 所以如果你自定義的控件也遵守這個(gè)約定, 那么就可以跟原生的控件進(jìn)行嵌套滑動(dòng)了.

基本原理

嵌套滑動(dòng)的基本原理是在子控件接收到滑動(dòng)一段距離的請求時(shí), 先詢問父控件是否要滑動(dòng), 如果滑動(dòng)了父控件就通知子控件它消耗了一部分滑動(dòng)距離, 子控件就只處理剩下的滑動(dòng)距離, 然后子控件滑動(dòng)完畢后再把剩余的滑動(dòng)距離傳給父控件.
通過這樣的嵌套滑動(dòng)機(jī)制, 在一次滑動(dòng)操作過程中

父控件和子控件都有機(jī)會(huì)對滑動(dòng)操作作出響應(yīng), 尤其父控件能夠分別在子控件處理滑動(dòng)距離之前和之后對滑動(dòng)距離進(jìn)行響應(yīng).

這解決了事件分發(fā)機(jī)制缺點(diǎn)引起的問題.

版本之別

在看具體的代碼之前先說下嵌套滑動(dòng)相關(guān)方法的一些我認(rèn)為值得注意的地方.

LOLLIPOP(SDK21)之后

為什么說這個(gè)是官方的解決方法? 因?yàn)?/p>

嵌套滑動(dòng)的相關(guān)邏輯作為普通方法直接寫進(jìn)了最新的(SDK21之后)ViewViewGroup類.

普通方法是指這個(gè)方法不是繼承自接口或者其他類, 例如[View#dispatchNestedScroll](https://developer.android.com/reference/android/view/View.html#dispatchNestedScroll(int, int, int, int, int[])), 可以看到官方標(biāo)注了Added in API level 21標(biāo)示, 也就是說這是在SDK21版本之后添加進(jìn)去的一個(gè)普通方法.

向前兼容

而SDK21之前的版本

官方在android.support.v4兼容包中提供了兩個(gè)接口NestedScrollingChildNestedScrollingParent, 還有兩個(gè)輔助類NestedScrollingChildHelperNestedScrollingParentHelper來幫助控件實(shí)現(xiàn)嵌套滑動(dòng).

這個(gè)兼容的原理很簡單

兩個(gè)接口NestedScrollingChildNestedScrollingParent分別定義上面提到的ViewViewParent新增的普通方法

在嵌套滑動(dòng)中會(huì)要求控件要么是繼承于SDK21之后的ViewViewGroup, 要么實(shí)現(xiàn)了這兩個(gè)接口, 這是控件能夠進(jìn)行嵌套滑動(dòng)的前提條件.
那么怎么知道調(diào)用的方法是控件自有的方法, 還是接口的方法? 在代碼中是通過ViewCompatViewParentCompat類來實(shí)現(xiàn).

ViewCompatViewParentCompat通過當(dāng)前的Build.VERSION.SDK_INT來判斷當(dāng)前版本, 然后選擇不同的實(shí)現(xiàn)類, 這樣就可以根據(jù)版本選擇調(diào)用的方法.

例如如果版本是SDK21之前, 那么就會(huì)判斷控件是否實(shí)現(xiàn)了接口, 然后調(diào)用接口的方法, 如果是SDK21之后, 那么就可以直接調(diào)用對應(yīng)的方法.

輔助類

除了接口兼容包還提供了NestedScrollingChildHelperNestedScrollingParentHelper兩個(gè)輔助類, 這兩個(gè)輔助類實(shí)際上就是對應(yīng)ViewViewParent中新增的普通方法, 代碼就不貼了, 簡單對比下就可以發(fā)現(xiàn), 對應(yīng)方法實(shí)現(xiàn)的邏輯基本一樣, 所以

只要在接口方法內(nèi)對應(yīng)調(diào)用輔助類的方法就可以兼容嵌套滑動(dòng)了.

例如在NestedScrollingChild#startNestedScroll方法中調(diào)用NestedScrollingChildHelper#startNestedScroll.
題外話: 這里實(shí)際用了代理模式來讓SDK21之前的控件具有了新增的方法.

默認(rèn)處理邏輯

雖然ViewViewGroup(SDK21之后)本身就具有嵌套滑動(dòng)的相關(guān)方法, 但是默認(rèn)情況是是不會(huì)被調(diào)用, 因?yàn)?code>View和ViewGroup本身不支持滑動(dòng), 所以

本身不支持滑動(dòng)的控件即使有嵌套滑動(dòng)的相關(guān)方法也不能進(jìn)行嵌套滑動(dòng).

上面已經(jīng)說到要讓控件支持嵌套滑動(dòng)

  1. 首先要控件類具有嵌套滑動(dòng)的相關(guān)方法, 要么僅支持SDK21之后版本, 要么實(shí)現(xiàn)對應(yīng)的接口, 為了兼容低版本, 更常用到的是后者.
  2. 因?yàn)槟J(rèn)的情況是不會(huì)支持滑動(dòng)的, 所以控件要在合適的位置主動(dòng)調(diào)起嵌套滑動(dòng)的方法.

接下來通過分析相對簡單的支持嵌套滑動(dòng)的容器NestedScrollView來了解下怎樣主動(dòng)調(diào)起嵌套滑動(dòng)的方法, 以及嵌套滑動(dòng)的具體邏輯.

相關(guān)方法

先簡單看看相關(guān)方法的作用, 更具體的說明建議看源碼注釋中的方法說明.
注意 : 下文分析用內(nèi)控件表示兩層嵌套中的子控件, 外控件表示嵌套中的父控件.**

NestedScrollingChild

startNestedScroll : 起始方法, 主要作用是找到接收滑動(dòng)距離信息的外控件.
dispatchNestedPreScroll : 在內(nèi)控件處理滑動(dòng)前把滑動(dòng)信息分發(fā)給外控件.
dispatchNestedScroll : 在內(nèi)控件處理完滑動(dòng)后把剩下的滑動(dòng)距離信息分發(fā)給外控件.
stopNestedScroll : 結(jié)束方法, 主要作用就是清空嵌套滑動(dòng)的相關(guān)狀態(tài)
setNestedScrollingEnabledisNestedScrollingEnabled : 一對get&set方法, 用來判斷控件是否支持嵌套滑動(dòng).
dispatchNestedPreFlingdispatchNestedFling : 跟Scroll的對應(yīng)方法作用類似, 不過分發(fā)的不是滑動(dòng)信息而是Fling信息.(這個(gè)Fling好難翻譯.. =挡爵。=)本文主要關(guān)注滑動(dòng)的處理, 所以后續(xù)不分析這兩個(gè)方法.

從方法名就可以看出

內(nèi)控件是嵌套滑動(dòng)的發(fā)起者.

NestedScrollingParent

因?yàn)閮?nèi)控件是發(fā)起者, 所以外控件的大部分方法都是被內(nèi)控件的對應(yīng)方法回調(diào)的.
onStartNestedScroll : 對應(yīng)startNestedScroll, 內(nèi)控件通過調(diào)用外控件的這個(gè)方法來確定外控件是否接收滑動(dòng)信息.
onNestedScrollAccepted : 當(dāng)外控件確定接收滑動(dòng)信息后該方法被回調(diào), 可以讓外控件針對嵌套滑動(dòng)做一些前期工作.
onNestedPreScroll : 關(guān)鍵方法, 接收內(nèi)控件處理滑動(dòng)前的滑動(dòng)距離信息, 在這里外控件可以優(yōu)先響應(yīng)滑動(dòng)操作, 消耗部分或者全部滑動(dòng)距離.
onNestedScroll : 關(guān)鍵方法, 接收內(nèi)控件處理完滑動(dòng)后的滑動(dòng)距離信息, 在這里外控件可以選擇是否處理剩余的滑動(dòng)距離.
onStopNestedScroll : 對應(yīng)stopNestedScroll, 用來做一些收尾工作.
getNestedScrollAxes : 返回嵌套滑動(dòng)的方向, 區(qū)分橫向滑動(dòng)和豎向滑動(dòng), 作用不大
onNestedPreFlingonNestedFling : 同上略

外控件通過onNestedPreScrollonNestedScroll來接收內(nèi)控件響應(yīng)滑動(dòng)前后的滑動(dòng)距離信息.

再次指出, 這兩個(gè)方法是實(shí)現(xiàn)嵌套滑動(dòng)效果的關(guān)鍵方法.

從NestedScrollView看嵌套機(jī)制

說完上面一大通, 終于可以開始分析源碼來了解嵌套滑動(dòng)機(jī)制起作用的具體邏輯了.
NestedScrollView簡單地說就是支持嵌套滑動(dòng)的ScrollView, 內(nèi)部邏輯簡單, 而且它既可以是內(nèi)控件, 也可以是外控件, 所以選擇分析它來了解嵌套滑動(dòng)機(jī)制.

注意 : 因?yàn)?code>NestedScrollingChildHelper和NestedScrollingParent這兩個(gè)輔助類的實(shí)現(xiàn)跟ViewViewGroup中的對應(yīng)方法是一樣的, 而且ViewViewGroup的源碼沒有使用兼容類, 所以下面分析相關(guān)方法的時(shí)候源碼都使用ViewViewGroup中的代碼.

上面已經(jīng)說了嵌套滑動(dòng)是從startNestedScroll開始, 所以先看看哪里調(diào)用了這個(gè)方法, 在源碼里一搜就能知道有兩個(gè)地方調(diào)用了這個(gè)方法.

  1. onInterceptTouchEventACTION_DOWN的情況
  2. onTouchEventACTION_DOWN的情況

因?yàn)?code>ACTION_DOWN是滑動(dòng)操作的開始事件, 所以當(dāng)接收到這個(gè)事件的時(shí)候嘗試找對應(yīng)的外控件. 只有找到了外控件才有后續(xù)的嵌套滑動(dòng)的邏輯發(fā)生.
關(guān)于NestedScrollView在這里的實(shí)現(xiàn)其實(shí)有個(gè)奇怪的地方, 提出一個(gè)問題, 不感興趣的可以直接跳過這段.

  • 既然內(nèi)控件是發(fā)起者, 為什么要在onInterceptTouchEvent也調(diào)用startNestedScroll呢?

因?yàn)槭录鬟f的時(shí)候會(huì)先執(zhí)行外控件的onInterceptTouchEvent, 也就是說第一個(gè)執(zhí)行startNestedScroll的是最外層的NestedScrollView, 即使它找到了對應(yīng)的外控件后續(xù)如果有子控件消費(fèi)了這個(gè)事件, 也就是說不執(zhí)行onTouchEvent方法, 那么找到外控件也沒用的, 不清楚設(shè)計(jì)者的意圖.

接著我們看startNestedScroll是如何找對應(yīng)的外控件的, 因?yàn)?code>NestedScrollView#startNestedScroll調(diào)用了輔助方法的startNestedScroll, 所以下面直接貼View#startNestedScroll.

// View.javapublic 
boolean startNestedScroll(int axes) { 
    // ... 
    if (isNestedScrollingEnabled()) { 
        ViewParent p = getParent(); 
        View child = this; 
        while (p != null) { 
            try { 
                // 關(guān)鍵代碼 
                if (p.onStartNestedScroll(child, this, axes)) { 
                    mNestedScrollingParent = p; 
                    p.onNestedScrollAccepted(child, this, axes); 
                    return true; 
                }
            } catch (AbstractMethodError e) { 
                // ... 
            } 
            if (p instanceof View) { 
                child = (View) p; 
            } 
            p = p.getParent(); 
        } 
    } 
    return false;
}

非常簡單的邏輯遍歷父控件, 調(diào)用父控件的onStartNestedScroll, 返回true表示找到了對應(yīng)的外控件, 找到外控件后馬上調(diào)用onNestedScrollAccepted

從這里可以知道

外控件不一定是內(nèi)控件的直接父控件, 但一定是最近的符合條件的外控件.

還可以確定了上面關(guān)于onStartNestedScroll的方法說明, 返回true表示接收內(nèi)控件的滑動(dòng)信息.對于NestedScrollView#onStartNestedScroll內(nèi)部邏輯很簡單, 只要是豎直滑動(dòng)方向就返回true, 所以可以知道

NestedScrollView不支持橫向嵌套滑動(dòng).

接著被調(diào)用的是onNestedScrollAccepted, 看NestedScrollView#onNestedScrollAccepted

// NestedScrollView.java
@Overridepublic void onNestedScrollAccepted(View child, View target, int nestedScrollAxes) { 
        mParentHelper.onNestedScrollAccepted(child, target, nestedScrollAxes); 
        startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL);
}

輔助類的方法很簡單, 就是記錄當(dāng)前的滑動(dòng)方向, 在這里NestedScrollView又調(diào)用startNestedScroll來找它自己的外控件, 這是為了連續(xù)嵌套NestedScrollView, 不過這是NestedScrollView自己的實(shí)現(xiàn), 不管它.

找到了外控件后ACTION_DOWN事件就沒嵌套滑動(dòng)的事了, 要滑動(dòng)肯定會(huì)在onTouchEvent中處理ACTION_MOVE事件, 接著我們看ACTION_MOVE事件是怎樣處理的.

// NestedScrollView#onTouchEvent
case MotionEvent.ACTION_MOVE: 
    // ... 
    final int y = (int) MotionEventCompat.getY(ev, activePointerIndex); 
    int deltaY = mLastMotionY - y; 
    // 讓外控件先處理滑動(dòng)距離 
    if (dispatchNestedPreScroll(0, deltaY, mScrollConsumed, mScrollOffset)) { 
        deltaY -= mScrollConsumed[1];// 消耗滑動(dòng)距離 
        // ... 
    } 
    // ... 
    if (mIsBeingDragged) { 
        // ... 
        // 內(nèi)控件處理滑動(dòng)距離 
        if (overScrollByCompat(0, deltaY, 0, getScrollY(), 0, range, 0, 
                      0, true) && !hasNestedScrollingParent()) { 
            // ... 
        } 

        final int scrolledDeltaY = getScrollY() - oldY; 
        final int unconsumedY = deltaY - scrolledDeltaY; 
        if (dispatchNestedScroll(0, scrolledDeltaY, 0, unconsumedY, mScrollOffset)) { 
            // ... 
        } 
        // ... 
    } 
    break;

這部分是NestedScrollView能夠處理嵌套滑動(dòng)的關(guān)鍵代碼了, 其他能夠嵌套滑動(dòng)的控件也應(yīng)該在ACTION_MOVE中類似地處理滑動(dòng)距離.

先計(jì)算出本次滑動(dòng)距離deltaY, 這里有個(gè)小細(xì)節(jié)

deltaY等于上一次的Y坐標(biāo)減去這次的Y坐標(biāo), 這意味著在相關(guān)方法中接收到的滑動(dòng)距離參數(shù)中, 滑動(dòng)距離 > 0表示手指向下滑動(dòng), 反之表示手指向上滑動(dòng). 這是因?yàn)樵谄聊恢衁軸正方向是向下的.

得到滑動(dòng)距離deltaY后, 先把它傳給dispatchNestedPreScroll, 然后在結(jié)果返回true的時(shí)候, delta會(huì)減去mScrollConsumed[1].

接著看dispatchNestedPreScroll干了什么

// View.java
public boolean dispatchNestedPreScroll(int dx, int dy,
                     @Nullable @Size(2) int[] consumed, @Nullable @Size(2) int[] offsetInWindow) {
    // ... 忽略狀態(tài)判斷 
    consumed[0] = 0; 
    consumed[1] = 0; 
    mNestedScrollingParent.onNestedPreScroll(this, dx, dy, consumed); 
    return consumed[0] != 0 || consumed[1] != 0; 
    // 其他情況返回false
}

忽略條件判斷和offsetInWindow的相關(guān)處理, 先指出consumed就是上一步分析中的mScrollConsumed, dy就是deltaY.
因?yàn)?code>dispatchNestedPreScroll的工作就是把滑動(dòng)距離在內(nèi)控件處理前分發(fā)給外控件, 所以這里的關(guān)鍵代碼也很簡單, 就是直接把相關(guān)的參數(shù)傳給外控件的onNestedPreScroll, 然后只要外控件消耗了滑動(dòng)距離(不論橫向還是豎向), 就會(huì)返回true

所以

外控件如果想在內(nèi)控件之前消耗滑動(dòng)距離僅需要在onNestedPreScroll把消耗的值放到數(shù)組中返回給內(nèi)控件.

onNestedPreScroll是決定外控件的嵌套滑動(dòng)邏輯的關(guān)鍵方法, 在不同的控件中應(yīng)該是根據(jù)需要有不同的實(shí)現(xiàn)的, 而在NestedScrollView中就是直接詢問它自己的外控件是否消耗滑動(dòng)距離, 實(shí)現(xiàn)比較簡單就不貼代碼了.

在這里提醒下, 在我們自己修改嵌套滑動(dòng)邏輯的時(shí)候需要注意滑動(dòng)距離的正負(fù)號(hào)和內(nèi)控件處理consumed數(shù)組的方式. 不過這些都是些數(shù)字游戲, 不細(xì)說了.

好了, 現(xiàn)在外控件已經(jīng)比內(nèi)控件先處理了滑動(dòng)距離了, 如果外控件沒有完全消耗掉所有滑動(dòng)距離, 這時(shí)該內(nèi)控件處理剩下的滑動(dòng)距離了, 不同的控件有不同的滑動(dòng)實(shí)現(xiàn), 在NestedScrollView中通過NestedScrollView#overScrollByCompat來進(jìn)行滑動(dòng), 并且滑動(dòng)結(jié)束后通過比對滑動(dòng)前后的scrollY值得到了內(nèi)控件消耗的滑動(dòng)距離, 然后得到剩下的滑動(dòng)距離, 最后傳給dispatchNestedScroll.

dispatchNestedScroll的邏輯跟dispatchNestedPreScroll幾乎一樣, 區(qū)別是它調(diào)用了外控件的onNestedScroll, 因?yàn)榈竭@里已經(jīng)是處理滑動(dòng)距離最后的機(jī)會(huì)了, 所以onNestedScroll不會(huì)再影響內(nèi)控件的處理邏輯了.

到這里ACTION_MOVE事件就分析完畢了.

最后就是stopNestedScroll了, 代碼就不貼了, 調(diào)用這個(gè)方法基本是新的滑動(dòng)操作開始前, 或者滑動(dòng)操作結(jié)束/取消, 代碼邏輯就是進(jìn)行一些變量的重置工作和調(diào)用onStopNestedScroll, 而onStopNestedScroll也類似.

整個(gè)嵌套滑動(dòng)的基本邏輯就是這樣. 注意這里雖然分析的是NestedScrollView, 但這代表了嵌套滑動(dòng)的"約定"處理方式, 雖然不同的控件實(shí)際的實(shí)現(xiàn)會(huì)有不同不過應(yīng)該遵循基本方法的調(diào)用順序, 確保參數(shù)的含義和參數(shù)的處理方式.

總結(jié)

  1. 如果要支持嵌套滑動(dòng), 內(nèi)控件和外控件要支持對應(yīng)的方法, 為了兼容低版本一般通過實(shí)現(xiàn)NestedScrollingChildNestedScrollingParent接口以及使用NestedScrollingChildHelperNestedScrollingParent輔助類.
  2. 具體嵌套滑動(dòng)邏輯主要是在onNestedPreScrollonNestedScroll方法中.
  3. 父控件通過給數(shù)組賦值來把消耗的滑動(dòng)距離傳遞給內(nèi)控件.

當(dāng)你希望滑動(dòng)內(nèi)部列表的時(shí)候先把列表頂部的控件隱藏掉, 例如ActionBar, 這時(shí)候嵌套滑動(dòng)就大有用處了, 具體的應(yīng)用場景可以看看Android 嵌套滑動(dòng)機(jī)制(NestedScrolling)的實(shí)現(xiàn)效果.

感覺這篇說得有些零碎, 如果有改進(jìn)的建議歡迎在討論區(qū)指出. :D

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末美尸,一起剝皮案震驚了整個(gè)濱河市褪猛,隨后出現(xiàn)的幾起案子艰垂,更是在濱河造成了極大的恐慌吸奴,老刑警劉巖斩狱,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件网沾,死亡現(xiàn)場離奇詭異,居然都是意外死亡壕吹,警方通過查閱死者的電腦和手機(jī)著蛙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來耳贬,“玉大人踏堡,你說我怎么就攤上這事⌒茫” “怎么了暂吉?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長缎患。 經(jīng)常有香客問我慕的,道長,這世上最難降的妖魔是什么挤渔? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任肮街,我火速辦了婚禮,結(jié)果婚禮上判导,老公的妹妹穿的比我還像新娘嫉父。我一直安慰自己沛硅,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布绕辖。 她就那樣靜靜地躺著摇肌,像睡著了一般。 火紅的嫁衣襯著肌膚如雪仪际。 梳的紋絲不亂的頭發(fā)上围小,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天,我揣著相機(jī)與錄音树碱,去河邊找鬼肯适。 笑死,一個(gè)胖子當(dāng)著我的面吹牛成榜,可吹牛的內(nèi)容都是我干的框舔。 我是一名探鬼主播,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼赎婚,長吁一口氣:“原來是場噩夢啊……” “哼刘绣!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起惑淳,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤额港,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后歧焦,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡肚医,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年绢馍,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片肠套。...
    茶點(diǎn)故事閱讀 40,102評論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡舰涌,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出你稚,到底是詐尸還是另有隱情瓷耙,我是刑警寧澤,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布刁赖,位于F島的核電站搁痛,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏宇弛。R本人自食惡果不足惜鸡典,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望枪芒。 院中可真熱鬧彻况,春花似錦谁尸、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至悍赢,卻和暖如春决瞳,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背泽裳。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工瞒斩, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人涮总。 一個(gè)月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓胸囱,卻偏偏與公主長得像,于是被迫代替她去往敵國和親瀑梗。 傳聞我的和親對象是個(gè)殘疾皇子烹笔,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評論 2 355

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