ViewPager源碼解析(一):onMeasure负溪、onLayout、populate

最近想擼一個(gè)垂直方向的VerticalViewPager济炎,如果想要把它做到屌川抡,那自然是要參考下現(xiàn)有我們的ViewPager實(shí)現(xiàn)。
該篇從ViewPager的measure與layout著手须尚,解讀ViewPager如何來實(shí)現(xiàn)自身已經(jīng)childView的測(cè)量與布局崖堤。

onMeasure()

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        //直接設(shè)置ViewPager自身的大小,這里可以看出ViewPager設(shè)置wrap_content時(shí)不起作用
        setMeasuredDimension(getDefaultSize(0, widthMeasureSpec),
                getDefaultSize(0, heightMeasureSpec));

        //mGutterSize是在事件分發(fā)時(shí)用來判斷是否拖拽的一個(gè)閾值耐床,影響是否攔截事件密幔。
        final int measuredWidth = getMeasuredWidth();
        final int maxGutterSize = measuredWidth / 10;
        mGutterSize = Math.min(maxGutterSize, mDefaultGutterSize);

        // 設(shè)置完寬高后計(jì)算出ViewPager實(shí)際可用顯示的寬高
        int childWidthSize = measuredWidth - getPaddingLeft() - getPaddingRight();
        int childHeightSize = getMeasuredHeight() - getPaddingTop() - getPaddingBottom();

        /**
         * 以下為測(cè)量DecorView的過程
         * 這里的 DecorView是ViewPager內(nèi)部定義的注解
         * ViewPager還可以通過xml布局添加item
         *     <ViewPager>
         *         <DecorView/>
         *     </ViewPager>
         * 所以DecorView是用來表示在xml布局中添加的子view,在xml布局中添加子view時(shí)需要添加@DecorView注解
         */
        int size = getChildCount();
        for (int i = 0; i < size; ++i) {
            final View child = getChildAt(i);
            if (child.getVisibility() != GONE) {
                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
                if (lp != null && lp.isDecor) {
                    //這里省略DecorView的具體測(cè)量過程
                    //…………
                    
                    //ViewPager實(shí)際可用顯示的寬高減去DecorView已占用的寬高
                    if (consumeVertical) {
                        childHeightSize -= child.getMeasuredHeight();
                    } else if (consumeHorizontal) {
                        childWidthSize -= child.getMeasuredWidth();
                    }
                }
            }
        }

        //計(jì)算去除decorView占位后的寬高測(cè)量模式(用于adapter中添加的ChildView)
        mChildWidthMeasureSpec = MeasureSpec.makeMeasureSpec(childWidthSize, MeasureSpec.EXACTLY);
        mChildHeightMeasureSpec = MeasureSpec.makeMeasureSpec(childHeightSize, MeasureSpec.EXACTLY);

        /**
         * populate()方法是ViewPager的一個(gè)核心方法了撩轰,它里面有矯正當(dāng)前ViewPager上需要加載的ItemInfo胯甩,
         * 下面會(huì)詳細(xì)分析【ItemInfo】^①和【populate()】^③
         */
        mInLayout = true;
        populate();
        mInLayout = false;

        /* *
         * 測(cè)量非DecorView的子View(通過Adapter添加的ChildView)
         * 上面通過populate()已經(jīng)矯正過需要加載的Item,
         * 所以下面的循環(huán)的getChildCount并不會(huì)等于DecorView的數(shù)量+Adapter.getCount()
         * (實(shí)際情況是: getChildCount() = DecorView數(shù)量+mItems.size()堪嫂,
         *   mItem中存放當(dāng)前ViewPager需要加載的ItemInfo偎箫,populate()后會(huì)得到最新的mItems)
         */
        size = getChildCount();
        for (int i = 0; i < size; ++i) {
            final View child = getChildAt(i);
            if (child.getVisibility() != GONE) {
                if (DEBUG) {
                    Log.v(TAG, "Measuring #" + i + " " + child + ": " + mChildWidthMeasureSpec);
                }

                //這里的LayoutParams為【ViewPager.LayoutParams】^②,下面會(huì)詳細(xì)分析
                final LayoutParams lp = (LayoutParams) child.getLayoutParams();

                //這個(gè)lp == null嚴(yán)重懷疑是個(gè)筆誤,應(yīng)該是lp != null
                //同時(shí)插一點(diǎn)lp.widthFactor皆串,可以看到該值會(huì)直接決定ViewPager中ChildView的顯示寬度淹办。
                //可以通過復(fù)寫Adapter.getPageWidth()方法返回值改變?cè)撝怠?                if (lp == null || !lp.isDecor) {
                    final int widthSpec = MeasureSpec.makeMeasureSpec(
                            (int) (childWidthSize * lp.widthFactor), MeasureSpec.EXACTLY);
                    child.measure(widthSpec, mChildHeightMeasureSpec);
                }
            }
        }
    }

總結(jié)一下onMeasure()的整體流程:
1、設(shè)置ViewPager自身的大杏拚健娇唯;
2、算出可用大小后為每個(gè)子DecorView測(cè)量寂玲;
3塔插、通過populate()方法調(diào)整ViewPager當(dāng)前需要加載的itemInfo
4、為當(dāng)前ViewPager加載的所有非DecorView測(cè)量拓哟。

接下來分析onMeasure預(yù)留的3個(gè)重要對(duì)象或方法:
①:ItemInfo
ItemInfo用來保存當(dāng)前ViewPager需要加載的子View的相關(guān)信息想许。

static class ItemInfo {
        Object object;//childView,該值為Adapter.instantiateItem()的返回值
        int position;//childView在viewPager中的位置
        boolean scrolling;//是否在滾動(dòng)
        float widthFactor;//寬度的占比断序,可以通過復(fù)寫Adapter.getPageWidth()方法返回值改變?cè)撝盗魑疲J(rèn)返回1。
        float offset;//頁面的偏移违诗,用來決定layout時(shí)childView的位置
    }

②:ViewPager.LayoutParams
ViewPager.LayoutParams中主要關(guān)心以下幾個(gè)重要字段漱凝。

         //是否為DecorView
        public boolean isDecor;

        // childView寬度與ViewPager寬度的比值(實(shí)際是可以超過1的)
        float widthFactor = 0.f;

        // 與Adapter中position相對(duì)應(yīng),DecorView不用關(guān)心該字段
        int position;

        // childView在mItems中的位置
        int childIndex;

③:populate()
populate()方法較長(zhǎng)诸迟,邏輯代碼較多茸炒,不過耐心點(diǎn)閱讀難度也不高愕乎。

void populate(int newCurrentItem) {
       /**
        *省略部分代碼
        *……
        */


        /**
         * 這里mOffscreenPageLimit決定緩存數(shù)量,
         * 可以通過setOffscreenPageLimit()方法來該表該值
         * mItems最多只會(huì)緩存1(mCurItem)+2*mOffscreenPageLimit個(gè)item,
         * mCurItem左右不足mOffscreenPageLimit個(gè)item時(shí)則達(dá)不到最大數(shù)量壁公。
         * 以下starPos與endPos分別表示mItems中緩存的起始與結(jié)束position
         */
        final int pageLimit = mOffscreenPageLimit;
        final int startPos = Math.max(0, mCurItem - pageLimit);
        final int N = mAdapter.getCount();
        final int endPos = Math.min(N - 1, mCurItem + pageLimit);

        if (N != mExpectedAdapterCount) {
            String resName;
            try {
                /**
                 * 知識(shí)點(diǎn)感论,系統(tǒng)提供了根據(jù)id獲取id名字的方法(之前傻傻反射去獲取)
                 */
                resName = getResources().getResourceName(getId());
            } catch (Resources.NotFoundException e) {
                resName = Integer.toHexString(getId());
            }
            throw new IllegalStateException("The application's PagerAdapter changed the adapter's"
                    + " contents without calling PagerAdapter#notifyDataSetChanged!"
                    + " Expected adapter item count: " + mExpectedAdapterCount + ", found: " + N
                    + " Pager id: " + resName
                    + " Pager class: " + getClass()
                    + " Problematic adapter: " + mAdapter.getClass());
        }

        /**
         * 在mItems中找到當(dāng)前的ItemInfo
         * 這里需要區(qū)分mCurItem和curIndex
         * mCurItem表示當(dāng)前item對(duì)應(yīng)所有item的position
         * curIndex表示當(dāng)前itemInfo對(duì)應(yīng)items中的位置
         */
        int curIndex = -1;
        ItemInfo curItem = null;
        for (curIndex = 0; curIndex < mItems.size(); curIndex++) {
            final ItemInfo ii = mItems.get(curIndex);
            if (ii.position >= mCurItem) {
                if (ii.position == mCurItem) curItem = ii;
                break;
            }
        }

        /**
         * 如果當(dāng)前未找到對(duì)應(yīng)的ItemInfo,
         * addNewItem()方法會(huì)調(diào)用Adapter.instantiateItem()方法往ViewPager中添加對(duì)應(yīng)mCurItem的childView,
         * 同時(shí)根據(jù)Adapter.instantiateItem()的返回值創(chuàng)建對(duì)應(yīng)的ItemInfo并添加到mItems
         */
        if (curItem == null && N > 0) {
            curItem = addNewItem(mCurItem, curIndex);
        }

        // Fill 3x the available width or up to the number of offscreen
        // pages requested to either side, whichever is larger.
        // If we have no current item we have no work to do.
        if (curItem != null) {
            float extraWidthLeft = 0.f;
            int itemIndex = curIndex - 1;
            //獲取當(dāng)前ItemInfo的左邊的ItemInfo li紊册,下面循環(huán)從這里開始
            ItemInfo ii = itemIndex >= 0 ? mItems.get(itemIndex) : null;
            //獲取到viewPager真實(shí)可用寬度
            final int clientWidth = getClientWidth();

            /**
             * leftWidthNeeded表示左邊打到回收條件的寬度比例的閾值比肄,
             * 以下處理三種場(chǎng)景
             * 場(chǎng)景1:當(dāng)左邊ItemInfo的extraWidthLeft超過leftWidthNeeded,
             *      且對(duì)應(yīng)的位置不在上面計(jì)算的startPos-endPos范圍內(nèi)時(shí)將從mItems中移除(如果存在)
             *      同時(shí)調(diào)用Adapter.destroyItem()通知Adapter回收itemInfo對(duì)應(yīng)的view
             * 場(chǎng)景2:在顯示范圍內(nèi)且存在囊陡,繼續(xù)下一次循環(huán)
             * 場(chǎng)景3:在顯示范圍內(nèi)且不存在芳绩,則通過addNewItem()方法調(diào)用Adapter.instantiateItem()方法
             *      往ViewPager中添加對(duì)應(yīng)mCurItem的childView,
             *      同時(shí)根據(jù)Adapter.instantiateItem()的返回值創(chuàng)建對(duì)應(yīng)的ItemInfo并添加到mItems
             */
            final float leftWidthNeeded = clientWidth <= 0 ? 0 :
                    2.f - curItem.widthFactor + (float) getPaddingLeft() / (float) clientWidth;

            /**
             * 遍歷mCurItem左邊的ItemInfo
             */
            for (int pos = mCurItem - 1; pos >= 0; pos--) {
                //場(chǎng)景1
                if (extraWidthLeft >= leftWidthNeeded && pos < startPos) {
                    if (ii == null) {
                        break;
                    }
                    if (pos == ii.position && !ii.scrolling) {
                        mItems.remove(itemIndex);
                        //移除緩存時(shí)回調(diào) mAdapter.destroyItem
                        mAdapter.destroyItem(this, pos, ii.object);
                        if (DEBUG) {
                            Log.i(TAG, "populate() - destroyItem() with pos: " + pos
                                    + " view: " + ((View) ii.object));
                        }
                        itemIndex--;
                        curIndex--;
                        ii = itemIndex >= 0 ? mItems.get(itemIndex) : null;
                    }
                    //場(chǎng)景2
                } else if (ii != null && pos == ii.position) {
                    extraWidthLeft += ii.widthFactor;
                    itemIndex--;
                    ii = itemIndex >= 0 ? mItems.get(itemIndex) : null;
                    //場(chǎng)景3
                } else {
                    ii = addNewItem(pos, itemIndex + 1);
                    extraWidthLeft += ii.widthFactor;
                    curIndex++;
                    ii = itemIndex >= 0 ? mItems.get(itemIndex) : null;
                }
            }

            /**
             * 以下為右遍歷,邏輯同以上左遍歷
             */
            float extraWidthRight = curItem.widthFactor;
            itemIndex = curIndex + 1;
            if (extraWidthRight < 2.f) {
                ii = itemIndex < mItems.size() ? mItems.get(itemIndex) : null;
                final float rightWidthNeeded = clientWidth <= 0 ? 0 :
                        (float) getPaddingRight() / (float) clientWidth + 2.f;
                for (int pos = mCurItem + 1; pos < N; pos++) {
                    if (extraWidthRight >= rightWidthNeeded && pos > endPos) {
                        if (ii == null) {
                            break;
                        }
                        if (pos == ii.position && !ii.scrolling) {
                            mItems.remove(itemIndex);
                            mAdapter.destroyItem(this, pos, ii.object);
                            if (DEBUG) {
                                Log.i(TAG, "populate() - destroyItem() with pos: " + pos
                                        + " view: " + ((View) ii.object));
                            }
                            ii = itemIndex < mItems.size() ? mItems.get(itemIndex) : null;
                        }
                    } else if (ii != null && pos == ii.position) {
                        extraWidthRight += ii.widthFactor;
                        itemIndex++;
                        ii = itemIndex < mItems.size() ? mItems.get(itemIndex) : null;
                    } else {
                        ii = addNewItem(pos, itemIndex);
                        itemIndex++;
                        extraWidthRight += ii.widthFactor;
                        ii = itemIndex < mItems.size() ? mItems.get(itemIndex) : null;
                    }
                }
            }

            /**
             * 這里在整理好緩存items后撞反,計(jì)算每個(gè)items里的{@link ItemInfo#offset}偏移量
             */
            calculatePageOffsets(curItem, curIndex, oldCurInfo);
        }

        /**
         * 這里是知識(shí)點(diǎn)示括,adapter會(huì)講當(dāng)前的{@link ItemInfo#object}通過setPrimaryItem回調(diào)給adapter,
         * 這個(gè)curItem.object就是Adapter中instantiateItem(ViewGroup container, int position)方法的返回值痢畜,一般都會(huì)返回我們添加的chilView
         * 所以在我們需要獲取ViewPager當(dāng)前的childView時(shí)垛膝,我們可以在Adapter中可以復(fù)寫setPrimaryItem方法將curItem.object保存起來,
         */
        mAdapter.setPrimaryItem(this, mCurItem, curItem != null ? curItem.object : null);

        mAdapter.finishUpdate(this);

        /**
         * 將以上更新的ItemInfo中的內(nèi)容更新到對(duì)應(yīng)childView的LayoutParams中
         */
        final int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            final View child = getChildAt(i);
            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
            lp.childIndex = i;
            if (!lp.isDecor && lp.widthFactor == 0.f) {
                // 0 means requery the adapter for this, it doesn't have a valid width.
                final ItemInfo ii = infoForChild(child);
                if (ii != null) {
                    lp.widthFactor = ii.widthFactor;
                    lp.position = ii.position;
                }
            }
        }
        sortChildDrawingOrder();

        //焦點(diǎn)傳遞
        if (hasFocus()) {
            View currentFocused = findFocus();
            ItemInfo ii = currentFocused != null ? infoForAnyChild(currentFocused) : null;
            if (ii == null || ii.position != mCurItem) {
                for (int i = 0; i < getChildCount(); i++) {
                    View child = getChildAt(i);
                    ii = infoForChild(child);
                    if (ii != null && ii.position == mCurItem) {
                        if (child.requestFocus(View.FOCUS_FORWARD)) {
                            break;
                        }
                    }
                }
            }
        }
    }

populate()代碼稍微有些長(zhǎng)丁稀,但是核心邏輯也不難理解吼拥,它實(shí)現(xiàn)了ViewPager的添加childView和刪除childView的功能,大致流程為:
1线衫、根據(jù)mOffscreenPageLimit計(jì)算mItems將要儲(chǔ)存ViewPager中startPos~endPos對(duì)應(yīng)的ItemInfo凿可;
2、從mItems中獲取當(dāng)前mCurItem對(duì)應(yīng)的ItemInfo授账,若沒有則通過addNewItem()方法調(diào)用Adapter.instantiateItem()為ViewPager添加mCurItem位置的childView枯跑。同時(shí)創(chuàng)建該位置對(duì)應(yīng)的ItemInfo并添加到mItems中;
3白热、循環(huán)遍歷當(dāng)前item左邊的所有item敛助,若不在leftWidthNeeded與startPos-endPos決定范圍內(nèi),則從mItems中刪除屋确,同時(shí)調(diào)用Adapter.destroyItem()方法來通知Adapter回收itemInfo對(duì)應(yīng)的view纳击。若在該范圍內(nèi)且不存在時(shí)則通過addNewItem()方法調(diào)用Adapter.instantiateItem()方法 往ViewPager中添加對(duì)應(yīng)mCurItem的childView,同時(shí)根據(jù)Adapter.instantiateItem()的返回值創(chuàng)建對(duì)應(yīng)的ItemInfo并添加到mItems;
4攻臀、循環(huán)遍歷當(dāng)前item右邊的所有item焕数,邏輯與左循環(huán)基本一致;
5刨啸、通過以上流程整理好需要加載的mItems后堡赔,計(jì)算mItems里的每個(gè)ItemInfo的偏移量,用來決定layout時(shí)的位置
6设联、最后將以上更新的ItemInfo中的內(nèi)容更新到對(duì)應(yīng)childView的LayoutParams中

populate()方法決定了ViewPager添加childView以及刪除childView善已,在整個(gè)ViewPager源碼中多個(gè)方法中調(diào)用存璃,有:onMeasure()、setAdapter()雕拼、onInterceptTouchEvent()、setAdapter()粘招、setPageTransformer()啥寇、setOffscreenPageLimit()、smoothScrollTo()等多個(gè)涉及到頁面發(fā)生變化的方法中調(diào)用洒扎,由此可見它在ViewPager中時(shí)非常重要的一個(gè)方法辑甜,所以好好理解它還是非常有必要的。

onLayout()

protected void onLayout(boolean changed, int l, int t, int r, int b) {
        //……略略略……

        // 首先對(duì)子DecorView進(jìn)行l(wèi)ayout
        for (int i = 0; i < count; i++) {
            final View child = getChildAt(i);
            if (child.getVisibility() != GONE) {
                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
                int childLeft = 0;
                int childTop = 0;
                if (lp.isDecor) {
                   //……略略略……

                    childLeft += scrollX;
                    child.layout(childLeft, childTop,
                            childLeft + child.getMeasuredWidth(),
                            childTop + child.getMeasuredHeight());
                    decorCount++;
                }
            }
        }

        //對(duì)Adapter中添加的childView進(jìn)行l(wèi)ayout
        final int childWidth = width - paddingLeft - paddingRight;
        // Page views. Do this once we have the right padding offsets from above.
        for (int i = 0; i < count; i++) {
            final View child = getChildAt(i);
            if (child.getVisibility() != GONE) {
                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
                ItemInfo ii;
                if (!lp.isDecor && (ii = infoForChild(child)) != null) {
                       //……略略略……

                    child.layout(childLeft, childTop,
                            childLeft + child.getMeasuredWidth(),
                            childTop + child.getMeasuredHeight());
                }
            }
        }
        mTopPageBounds = paddingTop;
        mBottomPageBounds = height - paddingBottom;
        mDecorChildCount = decorCount;

        //首次layout需要將viewPager滾動(dòng)到對(duì)應(yīng)的mCurItem位置
        if (mFirstLayout) {
            scrollToItem(mCurItem, false, 0, false);
        }
        mFirstLayout = false;
    }

可以看到onLayout()方法還是比較簡(jiǎn)單的袍冷,通過循環(huán)DecorView與非DecorView(Adapter中添加的childView)結(jié)合他們對(duì)應(yīng)的LayoutParams磷醋,對(duì)所有childView進(jìn)行l(wèi)ayout。

到這里ViewPager的onMeasure與onLayout就分析完了胡诗,重要的事情最后在提醒一次整個(gè)流程的重點(diǎn)就在populate()方法的流程邓线,populate()也是整個(gè)ViewPager的一個(gè)核心方法,還是要耐心將它讀完的煌恢。
到這里ViewPager的onMeasure骇陈、onLayout、populate就已經(jīng)分析完了瑰抵,下一篇《ViewPager源碼解析(二):setAdapter,notifyDataSetChanged》
會(huì)進(jìn)一步分析ViewPager的數(shù)據(jù)綁定與刷新你雌。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市二汛,隨后出現(xiàn)的幾起案子婿崭,更是在濱河造成了極大的恐慌,老刑警劉巖肴颊,帶你破解...
    沈念sama閱讀 221,635評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件氓栈,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡婿着,警方通過查閱死者的電腦和手機(jī)颤绕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來祟身,“玉大人奥务,你說我怎么就攤上這事⊥嗔颍” “怎么了氯葬?”我有些...
    開封第一講書人閱讀 168,083評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)婉陷。 經(jīng)常有香客問我帚称,道長(zhǎng)官研,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,640評(píng)論 1 296
  • 正文 為了忘掉前任闯睹,我火速辦了婚禮戏羽,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘楼吃。我一直安慰自己始花,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,640評(píng)論 6 397
  • 文/花漫 我一把揭開白布孩锡。 她就那樣靜靜地躺著酷宵,像睡著了一般。 火紅的嫁衣襯著肌膚如雪躬窜。 梳的紋絲不亂的頭發(fā)上浇垦,一...
    開封第一講書人閱讀 52,262評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音荣挨,去河邊找鬼男韧。 笑死,一個(gè)胖子當(dāng)著我的面吹牛默垄,可吹牛的內(nèi)容都是我干的煌抒。 我是一名探鬼主播,決...
    沈念sama閱讀 40,833評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼厕倍,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼寡壮!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起讹弯,我...
    開封第一講書人閱讀 39,736評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤况既,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后组民,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體棒仍,經(jīng)...
    沈念sama閱讀 46,280評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,369評(píng)論 3 340
  • 正文 我和宋清朗相戀三年臭胜,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了莫其。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,503評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡耸三,死狀恐怖乱陡,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情仪壮,我是刑警寧澤憨颠,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響爽彤,放射性物質(zhì)發(fā)生泄漏养盗。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,870評(píng)論 3 333
  • 文/蒙蒙 一适篙、第九天 我趴在偏房一處隱蔽的房頂上張望往核。 院中可真熱鬧,春花似錦嚷节、人聲如沸聂儒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至翁都,卻和暖如春碍论,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背柄慰。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工鳍悠, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人坐搔。 一個(gè)月前我還...
    沈念sama閱讀 48,909評(píng)論 3 376
  • 正文 我出身青樓藏研,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親概行。 傳聞我的和親對(duì)象是個(gè)殘疾皇子蠢挡,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,512評(píng)論 2 359