ViewPager源碼分析(3):與PagerAdapter 交互

我的CSDN博客同步發(fā)布:ViewPager源碼分析(3):與PagerAdapter 交互

轉(zhuǎn)載請(qǐng)注明出處:【huachao1001的簡(jiǎn)書:http://www.reibang.com/users/0a7e42698e4b/latest_articles】

我們知道络断,ViewPager顯示的頁面離不開我們定義的適配器辨萍,正是因?yàn)槲覀兙帉懥俗约旱倪m配器莽红,才讓ViewPager顯示出滿足你的需求的內(nèi)容嘱兼,那么ViewPager是如何與適配器(PagerAdapter)進(jìn)行交互的呢?我們今天來研讀一下ViewPager中與PagerAdapter交互的部分代碼及舍。本文對(duì)學(xué)習(xí)ViewPager很重要未辆,請(qǐng)耐心往下仔細(xì)研讀 ...O(∩_∩)O~~

在分析源碼之前,我們先看看ViewPager的最簡(jiǎn)單的示例用法击纬,當(dāng)然了,你也可以把你的所有View保存到一個(gè)List中钾麸。

先看看ViewPager簡(jiǎn)單使用

通過用法找到切入點(diǎn)更振,ViewPager的比較典型的示例用法如下:

public class MainActivity extends AppCompatActivity {

private List<String> data;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    init();


    ViewPager viewPager = (ViewPager) findViewById(R.id.vp);
    viewPager.setAdapter(new MyAdapter());
}

private void init() {
    data = new ArrayList<>();
    for (int i = 0; i < 10; i++) {
        data.add("str" + i);
    }
}

class MyAdapter extends PagerAdapter {

    @Override
    public int getCount() {
        return data.size();
    }

    @Override
    public boolean isViewFromObject(View view, Object object) {
        return view == object;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {

        container.removeView((View) object);
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        TextView textView = new TextView(MainActivity.this);
        String s = data.get(position);
        textView.setText(s);
        container.addView(textView);
        
        return textView;
    }
}
}

可以看到ViewPager與我們的數(shù)據(jù)源之間是需要通過適配器來適配的。接下來我們?nèi)タ纯?code>ViewPager源碼中饭尝,是如何與PagerAdapter交互肯腕。

從setAdapter切入

上面簡(jiǎn)單示例代碼中可用看到,調(diào)用ViewPagersetAdapter函數(shù)即可將ViewPagerPagerAdapter關(guān)聯(lián)起來钥平,我們先去查看ViewPagersetAdapter方法实撒。

public void setAdapter(PagerAdapter adapter) {
    //1.如果已經(jīng)設(shè)置過PagerAdapter,即mAdapter != null涉瘾,
    // 則做一些清理工作
    if (mAdapter != null) {
        //2.清除觀察者
        mAdapter.setViewPagerObserver(null);
        //3.回調(diào)startUpdate函數(shù)知态,告訴PagerAdapter開始更新要顯示的頁面
        mAdapter.startUpdate(this);
        //4.如果之前保存有頁面,則將之前所有的頁面destroy掉
        for (int i = 0; i < mItems.size(); i++) {
            final ItemInfo ii = mItems.get(i);
            mAdapter.destroyItem(this, ii.position, ii.object);
        }
        //5.回調(diào)finishUpdate立叛,告訴PagerAdapter結(jié)束更新
        mAdapter.finishUpdate(this);
        //6.將所有的頁面清除
        mItems.clear();
        //7.將所有的非Decor View移除负敏,即將頁面移除
        removeNonDecorViews();
        //8.當(dāng)前的顯示頁面重置到第一個(gè)
        mCurItem = 0;
        //9.滑動(dòng)重置到(0,0)位置
        scrollTo(0, 0);
    }

    //10.保存上一次的PagerAdapter
    final PagerAdapter oldAdapter = mAdapter;
    //11.設(shè)置mAdapter為新的PagerAdapter
    mAdapter = adapter;
    //12.設(shè)置期望的適配器中的頁面數(shù)量為0個(gè)
    mExpectedAdapterCount = 0;
    //13.如果設(shè)置的PagerAdapter不為null
    if (mAdapter != null) {
        //14.確保觀察者不為null,觀察者主要是用于監(jiān)視數(shù)據(jù)源的內(nèi)容發(fā)生變化
        if (mObserver == null) {
            mObserver = new PagerObserver();
        }
        //15.將觀察者設(shè)置到PagerAdapter中
        mAdapter.setViewPagerObserver(mObserver);
        mPopulatePending = false;
        //16.保存上一次是否是第一次Layout
        final boolean wasFirstLayout = mFirstLayout;
        //17.設(shè)定當(dāng)前為第一次Layout
        mFirstLayout = true;
        //18.更新期望的數(shù)據(jù)源中頁面?zhèn)€數(shù)
        mExpectedAdapterCount = mAdapter.getCount();
        //19.如果有數(shù)據(jù)需要恢復(fù)
        if (mRestoredCurItem >= 0) {
            //20.回調(diào)PagerAdapter的restoreState函數(shù)
            mAdapter.restoreState(mRestoredAdapterState, mRestoredClassLoader);
            setCurrentItemInternal(mRestoredCurItem, false, true);
            //21.標(biāo)記無需再恢復(fù)
            mRestoredCurItem = -1;
            mRestoredAdapterState = null;
            mRestoredClassLoader = null;
        } else if (!wasFirstLayout) {//如果在此之前不是第一次Layout
            //22.由于ViewPager并不是將所有頁面作為子View秘蛇,
            // 而是最多緩存用戶指定緩存?zhèn)€數(shù)*2(左右兩邊其做,可能左邊或右邊沒有那么多頁面)
            //因此需要?jiǎng)?chuàng)建和銷毀頁面,populate主要工作就是這些
            populate();
        } else {
            //23.重新布局(Layout)
            requestLayout();
        }
    }
    //24.如果PagerAdapter發(fā)生變化赁还,并且設(shè)置了OnAdapterChangeListener監(jiān)聽器
    // 則回調(diào)OnAdapterChangeListener的onAdapterChanged函數(shù)
    if (mAdapterChangeListener != null && oldAdapter != adapter) {
        mAdapterChangeListener.onAdapterChanged(oldAdapter, adapter);
    }
}

從第2條注解中看到妖泄,需要清除觀察者,另外從第14條注釋中看到艘策,需要設(shè)定觀察者蹈胡,那么這個(gè)觀察者是干嘛的呢?顯然,這里是通過使用觀察者模式审残。就是說梭域,當(dāng)我們編寫代碼時(shí),如果數(shù)據(jù)源發(fā)生變化搅轿,需要在代碼里調(diào)用PagerAdapter的notifyDataSetChanged函數(shù)病涨,即通知ViewPager數(shù)據(jù)源發(fā)生變化,ViewPager就是一個(gè)觀察者璧坟,通過觀察者類PagerObserver做相關(guān)應(yīng)對(duì)操作既穆。

另外,前面多次提到頁面的抽象描述類ItemInfo雀鹃,我們看看ItemInfo的定義:

static class ItemInfo {
    //object為PagerAdapter的instantiateItem函數(shù)返回的對(duì)象
    Object object;
    //position為頁面的序號(hào)幻工,即第幾個(gè)頁面
    int position;
    //是否正在滾動(dòng)
    boolean scrolling;
    //頁面寬度,取值為0到1黎茎,表示為頁面寬度與ViewPager顯示區(qū)域?qū)挾缺壤衣J(rèn)為1
    float widthFactor;
    //偏移量,頁面移動(dòng)的偏移量傅瞻,默認(rèn)為0
    float offset;
}

最后在第22條注釋中踢代,調(diào)用了populate()函數(shù),而populate()函數(shù)是做什么的呢嗅骄?可以說胳挎,我們?cè)谑褂?code>ViewPager之所以流暢不卡,絕大部分功勞屬于populate函數(shù)溺森。

大功臣populate函數(shù)

細(xì)心的童鞋會(huì)發(fā)現(xiàn)慕爬,早在上一篇文章《ViewPager源碼分析(2):滑動(dòng)及沖突處理 》2.3 ViewPager 定義smoothScrollTo函數(shù)小節(jié)源碼中的第33行中,就出現(xiàn)過populate函數(shù)屏积,無參數(shù)的populate其內(nèi)部是調(diào)用了有參的populate(int newCurrentItem)函數(shù)医窿,而newCurrentItem表示當(dāng)需要定位顯示的頁面。我們先看看源碼:

void populate(int newCurrentItem) {
    ItemInfo oldCurInfo = null;
    if (mCurItem != newCurrentItem) {
        oldCurInfo = infoForPosition(mCurItem);
        mCurItem = newCurrentItem;
    }

    if (mAdapter == null) {
        //對(duì)子View的繪制順序進(jìn)行排序炊林,優(yōu)先繪制Decor View
        //再按照position從小到大排序
        sortChildDrawingOrder();
        return;
    }

    //如果我們正在等待populate,那么在用戶手指抬起切換到新的位置期間應(yīng)該推遲創(chuàng)建子View留搔,
    // 直到滾動(dòng)到最終位置再去創(chuàng)建,以免在這個(gè)期間出現(xiàn)差錯(cuò)
    if (mPopulatePending) {
        if (DEBUG) Log.i(TAG, "populate is pending, skipping for now...");
        //對(duì)子View的繪制順序進(jìn)行排序铛铁,優(yōu)先繪制Decor View
        //再按照position從小到大排序
        sortChildDrawingOrder();
        return;
    }

    //同樣隔显,在ViewPager沒有attached到window之前,不要populate.
    // 這是因?yàn)槿绻覀冊(cè)诨謴?fù)View的層次結(jié)構(gòu)之前進(jìn)行populate饵逐,可能會(huì)與要恢復(fù)的內(nèi)容有沖突
    if (getWindowToken() == null) {
        return;
    }
    //回調(diào)PagerAdapter的startUpdate函數(shù)括眠,
    // 告訴PagerAdapter開始更新要顯示的頁面
    mAdapter.startUpdate(this);

    final int pageLimit = mOffscreenPageLimit;
    //確保起始位置大于等于0,如果用戶設(shè)置了緩存頁面數(shù)量倍权,第一個(gè)頁面為當(dāng)前頁面減去緩存頁面數(shù)量
    final int startPos = Math.max(0, mCurItem - pageLimit);
    //保存數(shù)據(jù)源中的數(shù)據(jù)個(gè)數(shù)
    final int N = mAdapter.getCount();
    //確保最后的位置小于等于數(shù)據(jù)源中數(shù)據(jù)個(gè)數(shù)-1掷豺,
    // 如果用戶設(shè)置了緩存頁面數(shù)量捞烟,第一個(gè)頁面為當(dāng)前頁面加緩存頁面數(shù)量
    final int endPos = Math.min(N - 1, mCurItem + pageLimit);

    //判斷用戶是否增減了數(shù)據(jù)源的元素,如果增減了且沒有調(diào)用notifyDataSetChanged当船,則拋出異常
    if (N != mExpectedAdapterCount) {
        //resName用于拋異常顯示
        String resName;
        try {
            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());
    }

    //定位到當(dāng)前獲焦的頁面题画,如果沒有的話,則添加一個(gè)
    int curIndex = -1;
    ItemInfo curItem = null;
    //遍歷每個(gè)頁面對(duì)應(yīng)的ItemInfo德频,找出獲焦頁面
    for (curIndex = 0; curIndex < mItems.size(); curIndex++) {
        final ItemInfo ii = mItems.get(curIndex);
        //找到當(dāng)前頁面對(duì)應(yīng)的ItemInfo后苍息,跳出循環(huán)
        if (ii.position >= mCurItem) {
            if (ii.position == mCurItem) curItem = ii;
            break;
        }
    }
    //如果沒有找到獲焦的頁面,說明mItems列表里面沒有保存獲焦頁面壹置,
    // 需要將獲焦頁面加入到mItems里面
    if (curItem == null && N > 0) {
        curItem = addNewItem(mCurItem, curIndex);
    }

    //默認(rèn)緩存當(dāng)前頁面的左右兩邊的頁面竞思,如果用戶設(shè)定了緩存頁面數(shù)量,
    // 則將當(dāng)前頁面兩邊都緩存用戶指定的數(shù)量的頁面
    //如果當(dāng)前沒有頁面钞护,則我們啥也不需要做
    if (curItem != null) {
        float extraWidthLeft = 0.f;
        //左邊的頁面
        int itemIndex = curIndex - 1;
        //如果當(dāng)前頁面左邊有頁面盖喷,則將左邊頁面對(duì)應(yīng)的ItemInfo取出,否則左邊頁面的ItemInfo為null
        ItemInfo ii = itemIndex >= 0 ? mItems.get(itemIndex) : null;
        //保存顯示區(qū)域的寬度
        final int clientWidth = getClientWidth();
        //算出左邊頁面需要的寬度难咕,注意课梳,這里的寬度是指實(shí)際寬度與可視區(qū)域?qū)挾缺壤?        // 即實(shí)際寬度=leftWidthNeeded*clientWidth
        final float leftWidthNeeded = clientWidth <= 0 ? 0 :
                2.f - curItem.widthFactor + (float) getPaddingLeft() / (float) clientWidth;
        //從當(dāng)前頁面左邊第一個(gè)頁面開始,左邊的頁面進(jìn)行遍歷
        for (int pos = mCurItem - 1; pos >= 0; pos--) {
            //如果左邊的寬度超過了所需的寬度余佃,并且當(dāng)前當(dāng)前頁面位置比第一個(gè)緩存頁面位置小
            //這說明這個(gè)頁面需要Destroy掉
            if (extraWidthLeft >= leftWidthNeeded && pos < startPos) {
                //如果左邊已經(jīng)沒有頁面了暮刃,跳出循環(huán)
                if (ii == null) {
                    break;
                }
                //將當(dāng)前頁面destroy掉
                if (pos == ii.position && !ii.scrolling) {
                    mItems.remove(itemIndex);
                    //回調(diào)PagerAdapter的destroyItem
                    mAdapter.destroyItem(this, pos, ii.object);
                    if (DEBUG) {
                        Log.i(TAG, "populate() - destroyItem() with pos: " + pos +
                                " view: " + ((View) ii.object));
                    }
                    //由于mItems刪除了一個(gè)元素
                    //需要將索引減一
                    itemIndex--;
                    curIndex--;
                    ii = itemIndex >= 0 ? mItems.get(itemIndex) : null;
                }
            } else if (ii != null && pos == ii.position) {
                //如果當(dāng)前位置是需要緩存的位置,并且這個(gè)位置上的頁面已經(jīng)存在
                //則將左邊寬度加上當(dāng)前位置的頁面
                extraWidthLeft += ii.widthFactor;
                //mItems往左遍歷
                itemIndex--;
                //ii設(shè)置為當(dāng)前遍歷的頁面的左邊一個(gè)頁面
                ii = itemIndex >= 0 ? mItems.get(itemIndex) : null;
            } else {//如果當(dāng)前位置是需要緩存咙冗,并且這個(gè)位置沒有頁面
                //需要添加一個(gè)ItemInfo,而addNewItem是通過PagerAdapter的instantiateItem獲取對(duì)象
                ii = addNewItem(pos, itemIndex + 1);
                //將左邊寬度加上當(dāng)前位置的頁面
                extraWidthLeft += ii.widthFactor;
                //由于新加了一個(gè)元素沾歪,當(dāng)前的索引號(hào)需要加1
                curIndex++;
                //ii設(shè)置為當(dāng)前遍歷的頁面的左邊一個(gè)頁面
                ii = itemIndex >= 0 ? mItems.get(itemIndex) : null;
            }
        }
        //同理漂彤,右邊需要添加緩存的頁面
        //......
        
       // 省略右邊添加緩存頁面代碼  
       
       //......

        calculatePageOffsets(curItem, curIndex, oldCurInfo);
    }

    if (DEBUG) {
        Log.i(TAG, "Current page list:");
        for (int i = 0; i < mItems.size(); i++) {
            Log.i(TAG, "#" + i + ": page " + mItems.get(i).position);
        }
    }
    //回調(diào)PagerAdapter的setPrimaryItem雾消,告訴PagerAdapter當(dāng)前顯示的頁面
    mAdapter.setPrimaryItem(this, mCurItem, curItem != null ? curItem.object : null);
    //回調(diào)PagerAdapter的finishUpdate,告訴PagerAdapter頁面更新結(jié)束
    mAdapter.finishUpdate(this);


    //檢查頁面的寬度是否測(cè)量挫望,如果頁面的LayoutParams數(shù)據(jù)沒有設(shè)定立润,則去重新設(shè)定好
    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;
            }
        }
    }
    //重新對(duì)頁面排序
    sortChildDrawingOrder();
    //如果ViewPager被設(shè)定為可獲焦的,則將當(dāng)前顯示的頁面設(shè)定為獲焦
    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函數(shù)源碼我們看到媳板,ViewPager緩存當(dāng)前顯示的頁面左右兩邊的頁面桑腮,這個(gè)頁面?zhèn)€數(shù)默認(rèn)為左右兩邊各1個(gè)(如果左右都有至少1個(gè)的話),或者是用戶通過調(diào)用ViewPager的setOffscreenPageLimit(int limit)函數(shù)來設(shè)定左右兩邊保持(好吧蛉幸,原諒我從頭到尾用緩存這個(gè)詞)的頁面?zhèn)€數(shù)破讨。看看setOffscreenPageLimit(int limit)源碼:

public void setOffscreenPageLimit(int limit) {
//DEFAULT_OFFSCREEN_PAGES=1
  if (limit < DEFAULT_OFFSCREEN_PAGES) {
      Log.w(TAG, "Requested offscreen page limit " + limit + " too small; defaulting to " +
              DEFAULT_OFFSCREEN_PAGES);
      limit = DEFAULT_OFFSCREEN_PAGES;
  }
  if (limit != mOffscreenPageLimit) {
      mOffscreenPageLimit = limit;
      populate();
  }
}

可以看到如果我們?cè)O(shè)置的值小于1奕纫,那么ViewPager會(huì)將緩存頁面數(shù)量設(shè)置為1提陶,即,緩存的頁面數(shù)量至少為1匹层,并且每次改變緩存數(shù)量后也會(huì)調(diào)用populate函數(shù)隙笆。

既然ViewPager真正的子View個(gè)數(shù)只是兩邊"緩存"的頁面?zhèn)€數(shù)+1(當(dāng)前顯示的頁面),那么ViewPager是如何做到無阻礙的從頭滑到尾而不出問題呢?前面我們提到撑柔,smoothScrollTo函數(shù)里面也調(diào)用了populate函數(shù)瘸爽,而populate函數(shù)維護(hù)了當(dāng)前顯示的頁面和左右兩邊“緩存”的頁面,這樣就做到了能滑到結(jié)尾铅忿。那么populate是如何讓ViewPager的子View一直保持為兩邊"緩存"的頁面+當(dāng)前顯示的頁面呢剪决?其實(shí),源碼上面很明顯辆沦,populate先判斷頁面是否不在緩存的范圍內(nèi)昼捍,如果不在緩存范圍內(nèi),則Destroy掉(調(diào)用PagerAdapterdestroyItem),而如果在緩存范圍肢扯,但是這個(gè)位置上頁面不存在(即沒有加入到ViewPager妒茬,作為ViewPager子View),則調(diào)用PagerAdapterinstantiateItem來添加新頁面(通過addNewItem來調(diào)用)蔚晨。假設(shè)我執(zhí)行了setOffscreenPageLimit(2)函數(shù)乍钻,那么我們看看ViewPager的簡(jiǎn)單示意圖:

ViewPager子View

如果你仔細(xì)想想,你會(huì)發(fā)現(xiàn)铭腕,這原理跟《打造屬于你的LayoutManager 》一文中的RecyclerView很像银择,不同的是,RecyclerView有回收利用累舷,而ViewPager沒有View的回收利用浩考。

好啦,今天的學(xué)習(xí)就到這里啦~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末被盈,一起剝皮案震驚了整個(gè)濱河市析孽,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌只怎,老刑警劉巖袜瞬,帶你破解...
    沈念sama閱讀 211,639評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異身堡,居然都是意外死亡邓尤,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,277評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門贴谎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來汞扎,“玉大人,你說我怎么就攤上這事擅这〕浩牵” “怎么了?”我有些...
    開封第一講書人閱讀 157,221評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵蕾哟,是天一觀的道長(zhǎng)一忱。 經(jīng)常有香客問我莲蜘,道長(zhǎng),這世上最難降的妖魔是什么帘营? 我笑而不...
    開封第一講書人閱讀 56,474評(píng)論 1 283
  • 正文 為了忘掉前任票渠,我火速辦了婚禮,結(jié)果婚禮上芬迄,老公的妹妹穿的比我還像新娘问顷。我一直安慰自己,他們只是感情好禀梳,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,570評(píng)論 6 386
  • 文/花漫 我一把揭開白布杜窄。 她就那樣靜靜地躺著,像睡著了一般算途。 火紅的嫁衣襯著肌膚如雪塞耕。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,816評(píng)論 1 290
  • 那天嘴瓤,我揣著相機(jī)與錄音扫外,去河邊找鬼。 笑死廓脆,一個(gè)胖子當(dāng)著我的面吹牛筛谚,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播停忿,決...
    沈念sama閱讀 38,957評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼驾讲,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了席赂?” 一聲冷哼從身側(cè)響起吮铭,我...
    開封第一講書人閱讀 37,718評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎氧枣,沒想到半個(gè)月后沐兵,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體别垮,經(jīng)...
    沈念sama閱讀 44,176評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡便监,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,511評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了碳想。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片烧董。...
    茶點(diǎn)故事閱讀 38,646評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖胧奔,靈堂內(nèi)的尸體忽然破棺而出逊移,到底是詐尸還是另有隱情,我是刑警寧澤龙填,帶...
    沈念sama閱讀 34,322評(píng)論 4 330
  • 正文 年R本政府宣布胳泉,位于F島的核電站拐叉,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏扇商。R本人自食惡果不足惜凤瘦,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,934評(píng)論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望案铺。 院中可真熱鬧蔬芥,春花似錦、人聲如沸控汉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,755評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽姑子。三九已至乎婿,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間街佑,已是汗流浹背次酌。 一陣腳步聲響...
    開封第一講書人閱讀 31,987評(píng)論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留舆乔,地道東北人岳服。 一個(gè)月前我還...
    沈念sama閱讀 46,358評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像希俩,于是被迫代替她去往敵國和親吊宋。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,514評(píng)論 2 348

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