Day28-find知乎CoordinatorLayout的實現(xiàn)

  • 本篇文章已授權(quán)微信公眾號 guolin_blog (郭霖)獨(dú)家發(fā)布

tips:
CoordinatorLayout(協(xié)調(diào)員布局)能實現(xiàn)一種類似協(xié)調(diào)視圖之間改變效果的布局

文章分為三部分

  1. CoordinatorLayout 概念的簡介
  2. 仿寫知乎自定義簡單behavior.(非gif演示)
  3. 完整的拆包分析知乎和仿寫知乎包括 MainActivity, 首頁回答的 ListFragment, 和點(diǎn)擊首頁回答打開的回答詳情的 DetailFragment. (gif演示的實現(xiàn))

比較啰嗦, 建議閱讀時間9分鐘.
可以直接跳到代碼部分

Demo地址

FindZhihu.gif

「協(xié)調(diào)員」CoordinatorLayout

「協(xié)調(diào)」概念

  1. 鋪墊: 正常的事件分發(fā)傳遞, 如果某一層 View 消費(fèi)掉了 Down事件. 之后的 MOVE, UP 事件都是傳遞到這一層, 無法直接實現(xiàn)子 view 和其他子 view的「協(xié)調(diào)」. 于是谷歌在15年的Design
    SupportLibrary包 Revision 22.2.0 加入了 CoordinatorLayout.
  2. 作為「協(xié)調(diào)員」的核心外部控件 CoordinatorLayout: 是實現(xiàn)了 NestedScrollingParent 接口的 ViewGroup. 根據(jù)谷歌的描述, 它作為一個頂級布局, 同時作為子視圖交互的容器 FrameLayout.
    那么它的子視圖有哪些呢?
    • 被依賴子View, 即可滾動的view, 實現(xiàn) NestedScrollingChild 的子 view .(RecyclerView已實現(xiàn))
    • 依賴子View, 如 AppbarLayout(隨著滑動的進(jìn)行而跟著操作的view)

而依賴子view 依靠 behavior 控制. 谷歌對于behavior的描述是: CoordinatorLayout的子view的交互行為插件, 一個 behavior 可以實現(xiàn)一個或者多個交互

Behavior 里的方法

分類1: view 監(jiān)聽另一個 view 的狀態(tài)變化, 比如大小, 位置, 顯示.

  1. layoutDependsOn 用來確定子View是否有另一個同級的View作為布局從屬
  2. onDependentViewChanged 響應(yīng)被依賴子View的變化

分類2: view 監(jiān)聽 CoordinatorLayout 里的滑動狀態(tài)

  1. onStartNestedScroll 我們要不要關(guān)心, 要關(guān)心什么樣的滑動
  2. onNestedPreScroll 在嵌套滑動進(jìn)行時,對象消費(fèi)滾動距離前回調(diào)(使用的最頻繁)

「協(xié)調(diào)」Demo-簡單Behavior鏈接


下面我們開始仿造知乎的詳情頁來自定義一個 Behavior

知乎Behavior歸納

1. MainActivity 里的 Listfragment(首頁)

  1. 滑動一定距離就隱藏
  2. 觸發(fā)效果后, 手不松, 在新的位置重置觸發(fā)第1步所需的距離
  3. 和點(diǎn)擊無關(guān)
    如果嘗試過默認(rèn)的behavior就會發(fā)現(xiàn)很像系統(tǒng)默認(rèn)的 behavior. app:layout_behavior="@string/appbar_scrolling_view_behavior"

2. 詳情 DetailFragment

  1. 文件內(nèi)容夠長才開啟滑動
  2. 剛進(jìn)來時底view顯示
  3. 快速滑動才顯示/隱藏view
  4. 底view隱藏時, 單擊顯示/隱藏頂和底view.
  5. 下拉到底放出頂和底view.

3. MainActivity 的底部 TabLayout 再分析

  1. 如果極慢的滑動會發(fā)現(xiàn), 對于同一個動畫效果, 底部 TabLayout先于頂部 Toolbar 執(zhí)行, 可以得出app第一頁所看到的頂部和底部不是一個behavior控制的

我們首先用behavior來寫一個仿知乎的效果

Demo-自定義Behavior, 仿知乎布局

  1. 創(chuàng)建 CoordinatorLayout 布局

    <CoordinatorLayout>
          <RecyclerView/>            
          <LinearLayout
            app:layout_behavior="@string/my_behavior"
            />          
    </CoordinatorLayout>
    

    如果一定要listview, 請判斷版本 api21 后再setNestedScrollingEnabled
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { listView.setNestedScrollingEnabled(true); }

  2. 自定義底部隱藏的動畫

    public class MyBehaviorAnim {
    
        private View mBottomView;
        private float mOriginalY;
    
        public BottomBehaviorAnim(View bottomView) {
            mBottomView = bottomView;
            mOriginalY = mBottomView.getY();
        }
    
    
        public void show() {
            ValueAnimator animator = ValueAnimator.ofFloat(mBottomView.getY(), mOriginalY);
            animator.setDuration(400);
            animator.setInterpolator(new LinearOutSlowInInterpolator());
            animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator valueAnimator) {
                    mBottomView.setY((Float) valueAnimator.getAnimatedValue());
                }
            });
            animator.start();
        }
    
    
        public void hide() {
            ValueAnimator animator = ValueAnimator.ofFloat(mBottomView.getY(), mOriginalY + mBottomView.getHeight());
            animator.setDuration(400);
            animator.setInterpolator(new LinearOutSlowInInterpolator());
            animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator valueAnimator) {
                    mBottomView.setY((Float) valueAnimator.getAnimatedValue());
                }
            });
            animator.start();
        }
    }
    
  1. 自定義底部隱藏的Behavior

    • 判斷手勢
    • 計算距離
    • 觸發(fā)動畫
    public class MyBehavior extends CoordinatorLayout.Behavior<View> {
    
        protected BottomBehaviorAnim mBottomAnim;
        private boolean isHide;
        private boolean canScroll = true;
        private int mTotalScrollY;
        protected boolean isInit = true; //防止new Anim導(dǎo)致的parent 和child坐標(biāo)變化
    
        private int mDuration = 400;
        private Interpolator mInterpolator = new LinearOutSlowInInterpolator();
        private int minScrollY = 5;//觸發(fā)滑動動畫最小距離
        private int scrollYDistance = 40;//設(shè)置最小滑動距離
    
        //1. 必須重寫兩個參數(shù)的構(gòu)造方法, 因為behavior的實例化?是反射這個構(gòu)造方法實現(xiàn)的
        public BottomBehavior(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        //2. 關(guān)心誰
        @Override
        public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
            return super.layoutDependsOn(parent, child, dependency);
        }
    
        /**
         * 觸發(fā)滑動嵌套滾動之前調(diào)用的方法
         *
         * @param coordinatorLayout coordinatorLayout父布局
         * @param child             使用Behavior的子View
         * @param target            觸發(fā)滑動嵌套的View(實現(xiàn)NestedScrollingChild接口)
         * @param dx                滑動的X軸距離
         * @param dy                滑動的Y軸距離
         * @param consumed          父布局消費(fèi)的滑動距離,consumed[0]和consumed[1]代表X和Y方向父布局消費(fèi)的距離,默認(rèn)為0
         */
        @Override
        public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, View child, View target,
                                      int dx, int dy, int[] consumed) {
            super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed);
        }
    
        /**
         * 滑動嵌套滾動時觸發(fā)的方法
         *
         * @param coordinatorLayout coordinatorLayout父布局
         * @param child             使用Behavior的子View
         * @param target            觸發(fā)滑動嵌套的View
         * @param dxConsumed        TargetView消費(fèi)的X軸距離
         * @param dyConsumed        TargetView消費(fèi)的Y軸距離
         * @param dxUnconsumed      未被TargetView消費(fèi)的X軸距離
         * @param dyUnconsumed      未被TargetView消費(fèi)的Y軸距離(如RecyclerView已經(jīng)到達(dá)頂部或底部,而用戶繼續(xù)滑動,此時dyUnconsumed的值不為0,可處理一些越界事件)
         */
        @Override
        public void onNestedScroll(CoordinatorLayout coordinatorLayout, View child, View target,
                                   int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
            super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed);
            if (canScroll) {
                mTotalScrollY += dyConsumed;
                if (Math.abs(dyConsumed) > minScrollY || Math.abs(mTotalScrollY) > scrollYDistance) {
                    if (dyConsumed < 0) {
                        if (isHide) {
                            mBottomAnim.show();
                            isHide = false;
                        }
                    } else if (dyConsumed > 0) {
                        if (!isHide) {
                            mBottomAnim.hide();
                            isHide = true;
                        }
                    }
                    mTotalScrollY = 0;
                }
            }
        }
    
        @Override
        public boolean onStartNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull View child, @NonNull View directTargetChild, @NonNull View target, int nestedScrollAxes) {
            if (isInit) {
                mBottomAnim = new BottomBehaviorAnim(child);
                isInit = false;
            }
            return (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0;
        }
    
        @Override
        public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, View child, final View target) {
            super.onStopNestedScroll(coordinatorLayout, child, target);
        }
    
    }
    
  2. 關(guān)聯(lián)xml和behavior
    先在 string 里添加 behavior, 再在依賴子 view 中添加

    <resources>
        <string name="my_behavior">com.clickdemo.Behavior.MyBehavior</string>
    </resources>
    
    <LinearLayout
      app:layout_behavior="@string/my_behavior"
      />    
    

思考: 自定義behavior確實能做到類似知乎的隱藏 TabView 的效果, 但是知乎真的是這么實現(xiàn)的嗎? Activity 和 Fragment 都用上了 CoordinatorLayout?

知乎

知乎拆包靜態(tài)分析.

  1. 準(zhǔn)備
    • 分析對象: 知乎的首頁和回答詳情頁.
    • 材料:
      • 拆包拿到的xml等文件.(4.1.8包來源于酷安app端的歷史版本)
      • Luyten (查看.jar文件里的混淆后的java代碼)
      • Hierarchy View (AndroidStudio視圖查看工具)
    • 前提: 拆包拿到的activity_main.xml中可以看到, 底部的 com.zhihu.android.base.widget.ZHTabLayout 用到了 layout_behavior.
    <?xml version="1.0" encoding="utf-8"?>
    <com.zhihu.android.app.ui.widget.ZHInsetsFrameLayout android:id="@id/content_container" android:tag="layout/activity_main_0" android:layout_width="fill_parent" android:layout_height="fill_parent"
      xmlns:android="http://schemas.android.com/apk/res/android" xmlns:zhihu="http://schemas.android.com/apk/res-auto">
        <com.zhihu.android.app.ui.widget.reveal.widget.RevealFrameLayout android:background="?zhihu.background.window" android:layout_width="fill_parent" android:layout_height="fill_parent">
            <android.support.design.widget.CoordinatorLayout android:id="@id/coordinator_layout" android:layout_width="fill_parent" android:layout_height="fill_parent">
                <com.zhihu.android.base.widget.NonSwipeableViewPager android:id="@id/main_pager" android:layout_width="fill_parent" android:layout_height="fill_parent" />
                <FrameLayout android:id="@android:id/content" android:layout_width="fill_parent" android:layout_height="fill_parent" zhihu:layout_behavior="com.zhihu.android.base.widget.SnackBarBehavior" zhihu:layout_anchorGravity="end|center|bottom" />
                <com.zhihu.android.base.widget.ZHTabLayout android:layout_gravity="end|bottom|center" android:id="@id/main_tab" android:background="?zhihu.background.navigation.tab.bottom" android:layout_width="fill_parent" android:layout_height="@dimen/bottom_navigation_height" zhihu:layout_behavior="com.zhihu.android.base.widget.FooterBehavior" zhihu:layout_anchorGravity="end|center|bottom" zhihu:tabIndicatorColor="@android:color/transparent" zhihu:tabGravity="fill" />
            </android.support.design.widget.CoordinatorLayout>
        </com.zhihu.android.app.ui.widget.reveal.widget.RevealFrameLayout>
        <com.zhihu.android.base.widget.ZHFrameLayout android:id="@id/overlay_container" android:layout_width="fill_parent" android:layout_height="fill_parent" /
    </com.zhihu.android.app.ui.widget.ZHInsetsFrameLayout>
    
    
    • 推測: MainActivity 包著四個Fragment, 底部依靠「tabLayout」切換, 然后進(jìn)入詳情頁把內(nèi)容都換一遍, 然后每一頁一個 CoordinatorLayout?
    • 目的: 查看知乎Fragment頁的behavior如何實現(xiàn)的 拿到詳情和首頁Fragment的xml和java文件
  2. 用 Hierarchy View 拿到 view 的 id
    模擬器打開app, 進(jìn)入首頁的一個回答下, 用 Hierarchy View (在Android Studio自帶的tool -> Android Device Monitor) 發(fā)現(xiàn)是 CoordinatorLayout 包著 NonSwipeableViewPager + +ZHFrameLayout + ZHTabLayout , 之后進(jìn)入詳情頁也是替換了 NonSwipeableViewPager 里的 FrameLayout.
    image

    activity_main的效果如上圖, 此時的FrameLayout顯示的是首頁ListFragment
    image

    上圖是FrameLayout顯示的是回答詳情DetailFragment

疑問1: 等等, 只有一個 CoordinatorLayout?
之前猜的是有多個CoordinatorLayout啊?

常規(guī)使用下, CoordinatorLayout 的子view的子view,只能跟著上一級view整體實現(xiàn)協(xié)調(diào), 即使AppBarLayout, 協(xié)調(diào)的效果也是固定的幾種

所以知乎 Fragment 里底部的view和頭部的toolbar是不依賴 CoordinatorLayout 進(jìn)行協(xié)調(diào)的?
還是先找到Activity和Fragment.java文件吧

  1. 根據(jù) view 的 id 來找在哪些 xml 用到了它
    接下來我們用之前反編譯好的資源開始尋找, 根據(jù)第1步, 我們看到了一個看似根布局的view, FrameInterceptLayout. 那就在res/layout里搜索下. 果然用到 FrameInterceptLayout 的地方不是很多. 而這 fragment_pager 和fragment_pager_2 看起來很可疑.

    image

  2. 根據(jù) xml 的 id 來找在哪些 java 文件里用到了它
    那接下來看下哪些地方用到了「fragment_pager」, 通過Android逆向之旅 可以得出: apk被反編譯后會產(chǎn)生一個至關(guān)重要的 public.xml 文件, 就在res/values/public.xml下, 打開后搜一下, 哈, 找到你了, 面碼, 0x7f0400be0x7f0400bf.

    image

    但是這是倆 十六進(jìn)制 的東西啊. 對, Android逆向之旅里也告訴我們怎么看 0x7f0400be:

    這里可以看到,一個id字段,都有對應(yīng)的類型,名稱,和id值的
    而這里的id值是一個整型值,8個字節(jié);由三部分組成的:
    PackageId+TypeId+EntryId
    PackageId:是包的Id值,Android中如果是第三方應(yīng)用的話,這個值默認(rèn)就是0x7F,系統(tǒng)應(yīng)用的話就是0x01,具體我們可以后面看aapt源碼得知,他占用兩個字節(jié)。
    TypeId:是資源的類型Id值,一般Android中有這幾個類型:attr,drawable,layout,dimen,string,style等,而且這些類型的值是從1開始逐漸遞增的,而且順序不能改變,attr=0x01,drawable=0x02….他占用兩個字節(jié)。
    EntryId:是在具體的類型下資源實體的id值,從0開始,依次遞增,他占用四個字節(jié)。

    那就用計算器轉(zhuǎn)換一下, 拿到十進(jìn)制的 2130968766 和 2130968767
    然后打開Luyten(替代JD_GUI, 有些文件JD_GUI打開后是一片空白). 打開一個jar文件, 先搜索一下 2130968766, 找到了.


    image

    看到databinding感嘆下, 原來知乎早就上了databinding.

    不過我們要的是下面這個com/zhihu/app/ui/fragment/b/i.class...
    找到了這句話

    public View a(final LayoutInflater layoutInflater, final ViewGroup viewGroup, final Bundle bundle) {
        this.b = android.databinding.e.a(layoutInflater, 2130968766, viewGroup, false);
        return this.b.h();
    }
    

    可以猜到這句話就是 fragment 的 onCreateView 啦
    這就拿到了主頁Fragment的java文件和xml文件了.
    同理拿到了 詳情頁的ZHObservableWebView, 首頁列表的ZHRecyclerView extends ObservableRecyclerView. 這個頻繁出現(xiàn)的ObservableXXXView 根據(jù)包名, 可以找到這個項目ksoichiro/Android-ObservableScrollView

當(dāng)然了, 如果在第1步的HierarchyView里能找到特殊的id的view可以直接搜索, 比如詳情頁可以靠fragment_paging_layout搜到.詳情頁的view是fragment_paging.xml, 如下圖


image

破案了! 結(jié)合疑問1, 可以得出:
知乎的 Fragment 們的協(xié)調(diào)不是通過 CoordinatorLayout 的 behavior, 而是使用了觀察者模式的開源項目.

總結(jié)一下(省略了過程):
  1. MainActivity 的底部 TabLayout 使用的是 CoordinatorLayout 的 layout_behavior
  2. 首頁ListFragment 的頂部toolbar可能是 ZHObservableRecyclerView 也可能是 FrameInterceptLayout控制, xml文件是 fragment_paging, Java 文件在
  3. 首頁詳情DetailFragment 的頂部和底部是 ZHObservableWebView 控制 xml文件是 fragment_pager, Java 文件在com/zhihu/app/ui/fragment/b/i.class

為啥首頁詳情頁這么肯定是 ZHObservableWebView 控制的呢, 因為在Luyten里查看到的知乎源碼中重寫了onScrollChange 方法, 多返回了int l, t, oldl, oldt 四個參數(shù), 而我自己在寫的時候發(fā)現(xiàn)也需要這四個參數(shù)才方便實現(xiàn)結(jié)果.

大致效果如圖:


image

具體Demo 地址

核心部分原理描述:
從 MainActivity 的 ListFragment 切換到 DetailFragment 時, 把 ListFragment 的 toolbar 和 MainActivity 的 tabLayout 恢復(fù)原樣(先GONE掉).
從 DetailFragment 切換回 ListFragment 時, 直接VISIBLE 底部tabLayout.

彎路

  1. 如何只在快速下滑時才觸發(fā) behavior 的 onNestedScroll(仿知乎詳情頁)
    • 原思路: 通過event監(jiān)聽手勢Y軸速度. 傳給behavior. 又因為「協(xié)調(diào)」時 onTouchEvent 無法接收到事件, behavior不屬于ViewGroup, 無法在 behavior 里調(diào)用 dispatchTouchEvent, 只能在Activity里調(diào), 所以采用 activity 監(jiān)聽的dispatchTouchEvent里event的 Y 軸速度, 再回調(diào)給behavior.
    • 現(xiàn)思路: 其實 onNestedScroll 的參數(shù) dyConsumed (Y 軸偏移量的大小)就是速度...
  2. 如何判斷滑到底
    • 原思路: 在 「協(xié)調(diào)」監(jiān)聽 onNestedScroll 里, dyConsumed == 0 時表示滑到邊界了, dyUnconsumed > 0 表示滑到邊界了還在下拉, 所以通過(dyConsumed == 0 && dyUnconsumed > 0)來判斷
    • 現(xiàn)思路: boolean view.canScrollVertically(1) 的返回值表示能夠下拉, -1的返回值表示上拉
  3. 詳情Fragment覆蓋了本該在前面的TabLayout, 即如何讓xml中被TabLayout擋住的ViewPager, 反過來擋住 TabLayout.
    • 原思路: 看到源代碼中 afollestad/material-dialogs, 誤以為新開的Fragment都是 DialogFragment
    • 原思路: 通過 View.bringToFront
    • 現(xiàn)思路:
      1. DialogFragment 在Hierarchy View里能看到多開了一個 MainActivity 進(jìn)程. 里面重新從 DecorView 開始的布局,
      2. View.bringToFront 即使不開啟新的 DecorView, 也改變了原進(jìn)程, 無法在Hierarchy View上與知乎的布局一樣.
  4. 為啥不直接使用 dispatchTouchEvent
    • behavior 里沒有重寫 dispatchTouchEvent
  5. 為啥不 onTouchEvent
    • behavior 里在拿到一段ACTION_MOVE后會攔截掉直接扔給 Behavior 一個 ACTION_CANCEL

參考

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,048評論 6 542
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,414評論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,169評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,722評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,465評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,823評論 1 328
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,813評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,000評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,554評論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,295評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,513評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,035評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,722評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,125評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,430評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,237評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,482評論 2 379