RecyclerView回收機(jī)制分析


Recycler類(lèi)是RecyclerView內(nèi)部final類(lèi),它管理scrapped(廢棄)或detached(獨(dú)立)的Item視圖缀蹄,使它們可以重用峭跳。我們都知道,在ListView中缺前,也有一個(gè)類(lèi)似的RecycleBin類(lèi)蛀醉,管理Item的重用。本文的重點(diǎn)是Recycler類(lèi)诡延,分析一下視圖在消失與出現(xiàn)時(shí)滞欠,如何利用Recycler實(shí)現(xiàn)重用。
ViewHolder類(lèi)RecyclerView的內(nèi)部抽象類(lèi)肆良,我們自己定義的Adapter中實(shí)現(xiàn)筛璧,封裝子視圖的一些視圖。


Scrapped視圖

先看一下Recycler內(nèi)部的幾個(gè)引用惹恃。

final ArrayList<ViewHolder> mAttachedScrap = new ArrayList<>();
final ArrayList<ViewHolder> mCachedViews = new ArrayList<ViewHolder>();
private RecycledViewPool mRecyclerPool;
private ViewCacheExtension mViewCacheExtension;

mAttachedScrap列表:用來(lái)存儲(chǔ)Scrapped(廢棄)的ViewHolder夭谤,它對(duì)應(yīng)的視圖是detached的,即ItemView調(diào)用了ViewGroup的detachViewFromParent方法巫糙,從容器的子視圖數(shù)組中移除朗儒,它其實(shí)并沒(méi)有被廢棄。它正是存放從RecyclerView中detached的ItemView的ViewHolder列表。

當(dāng)RecyclerView初始加載Item醉锄,第一次觸發(fā)onLayoutChildren時(shí)乏悄,fill創(chuàng)建滿足RecyclerView高度的子ItemView,ViewHolder綁定ItemView恳不,并ViewGroup#addView加入RecyclerView視圖檩小。第二次onLayoutChildren時(shí),通過(guò)detachAndScrapAttachedViews方法將全部ItemView從mChildren數(shù)組刪除烟勋,觸發(fā)的是ViewGroup#detachViewFromParent方法规求,ItemView變?yōu)閐etached,ViewHolder放入mAttachedScrap卵惦,fill繼續(xù)觸發(fā)從mAttachedScrap中獲取ViewHolder阻肿,將ViewHolder加入子View數(shù)組,觸發(fā)的是ViewGroup#attachViewToParent方法沮尿。

綜上所述:mAttachedScrap列表只是暫存從RecyclerView容器中detached下來(lái)的ItemView丛塌,也可以說(shuō)從mChildren數(shù)組移除的ItemView,這些ItemView屬于Scrapped蛹找,但是立馬又會(huì)被attach到RecyclerView姨伤。

mCachedViews列表:從RecyclerView區(qū)域移除,從ViewGroup中刪除的ItemView庸疾,存儲(chǔ)在列表中,最大值max当编,大于max時(shí)届慈,刪除最早進(jìn)入的第0個(gè)元素,該元素放入RecycledViewPool中忿偷,如果還是放不下金顿,直接放入RecycledViewPool。永遠(yuǎn)存儲(chǔ)最新從RecyclerView刪除的視圖ViewHolder鲤桥。
ViewGroup已經(jīng)執(zhí)行過(guò)removeViewAt刪除了View揍拆。
RecycledViewPool:視圖緩存池,當(dāng)mCachedViews存儲(chǔ)不下時(shí)茶凳,將ViewHolder放入嫂拴,根據(jù)類(lèi)型存儲(chǔ)。ViewGroup已經(jīng)執(zhí)行過(guò)removeViewAt刪除了View贮喧。
ViewCacheExtension:擴(kuò)展使用筒狠,開(kāi)發(fā)者自己控制緩存。

RecycView.png

圖中的數(shù)據(jù)源一共有17項(xiàng)箱沦,顯示區(qū)域中辩恼,可容納的子視圖大約在12個(gè)左右。


ItemView視圖消失邏輯

RecyclerView視圖顯示出來(lái)以后,手指觸屏灶伊,向上滑動(dòng)疆前。此時(shí),position是0,1,2,3...的ItemView依次滾動(dòng)出視圖可見(jiàn)范圍聘萨。
通過(guò)源碼調(diào)試峡继,發(fā)現(xiàn)在LinearLayoutManager的recycleChildren方法處,觸發(fā)了下面的方法匈挖,定義在LayoutManager類(lèi)碾牌。

public void removeAndRecycleViewAt(int index, Recycler recycler) {
    final View view = getChildAt(index);
    removeViewAt(index);//從父容器中刪除。
    recycler.recycleView(view);//存入Recycler
}

首先儡循,LayoutManager的removeViewAt方法舶吗,從RecyclerView中刪除索引index的子視圖,它與position無(wú)關(guān)择膝。調(diào)用輔助類(lèi)ChildHelper的removeViewAt方法誓琼。

public void removeViewAt(int index) {
    final View child = getChildAt(index);
    if (child != null) {
        mChildHelper.removeViewAt(index);
    }
}

RecyclerView類(lèi)的初始化initChildrenHelper方法,定義Callback對(duì)象肴捉,在輔助類(lèi)的方法中腹侣,調(diào)用內(nèi)部Callback的對(duì)應(yīng)方法。

private void initChildrenHelper() {
    mChildHelper = new ChildHelper(new ChildHelper.Callback() {
        ...
        @Override
        public void removeViewAt(int index) {
            final View child = RecyclerView.this.getChildAt(index);
            if (child != null) {
                dispatchChildDetached(child);
            }
            RecyclerView.this.removeViewAt(index);
        }
        ...
          
    });
}

dispatchChildDetached方法齿穗,通知子視圖detached傲隶,將調(diào)用Adapter的onViewDetachedFromWindow方法,可以在自己的Adapter中重寫(xiě)窃页。注意跺株,這里并沒(méi)有觸發(fā)ViewGroup的detachViewFromParent方法。
RecyclerView的removeViewAt方法脖卖,調(diào)用父類(lèi)ViewGroup的removeViewAt方法乒省,刪除該ItemView子視圖。
手指上滑畦木,每次最頂部Item視圖滑出屏幕時(shí)袖扛,刪除的都是index是0的子視圖,手指下移十籍,每次底部Item視圖滑出可視范圍草娜,刪除的都是index是12左右的子視圖矾睦,與position無(wú)關(guān)拐揭。
其次玄窝,調(diào)用Recycler的recycleView方法,將ViewHolder加入緩存mCachedViews或RecycledViewPool池械姻。

 public void recycleView(View view) {
    ViewHolder holder = getChildViewHolderInt(view);
    if (holder.isTmpDetached()) {
        removeDetachedView(view, false);
    }
    if (holder.isScrap()) {
        holder.unScrap();
    } else if (holder.wasReturnedFromScrap()){
        holder.clearReturnedFromScrapFlag();
    }
    recycleViewHolderInternal(holder);
}

根據(jù)View獲取它綁定的ViewHolder對(duì)象妒蛇,從View的LayoutParams中獲取机断。ViewHolder的內(nèi)部mScrapContainer(即Recycler)是空,isScrap方法返回false绣夺。只有執(zhí)行過(guò)Recycler的scrapView(View)方法吏奸,將ViewHolder加入到mAttachedScrap列表時(shí),才會(huì)設(shè)置內(nèi)部mScrapContainer值陶耍,當(dāng)isScrap返回true時(shí)奋蔚,調(diào)用unScrap方法,調(diào)用內(nèi)部Recycler的unscrapView方法烈钞。

void unscrapView(ViewHolder holder) {
    if (holder.mInChangeScrap) {
        mChangedScrap.remove(holder);
    } else {
        mAttachedScrap.remove(holder);
    }
    holder.mScrapContainer = null;
    holder.mInChangeScrap = false;
    holder.clearReturnedFromScrapFlag();
}

從mAttachedScrap列表中刪除泊碑,置空ViewHolder內(nèi)部Recycler。
Recycler的recycleViewHolderInternal方法毯欣,將ViewHolder加入緩存mCachedViews或RecycledViewPool池馒过。

void recycleViewHolderInternal(ViewHolder holder) {
    ...
    final boolean transientStatePreventsRecycling = holder
                    .doesTransientStatePreventRecycling();
    final boolean forceRecycle = mAdapter != null
                    && transientStatePreventsRecycling
                    && mAdapter.onFailedToRecycleView(holder);
    boolean cached = false;
    boolean recycled = false;
    if (forceRecycle || holder.isRecyclable()) {
        if (!holder.hasAnyOfTheFlags(ViewHolder.FLAG_INVALID
                        | ViewHolder.FLAG_REMOVED
                        | ViewHolder.FLAG_UPDATE)) {
            int cachedViewSize = mCachedViews.size();
            if (cachedViewSize >= mViewCacheMax && cachedViewSize > 0) {
                //刪除mCachedViews第0個(gè)元素,并觸發(fā)
                //addViewHolderToRecycledViewPool方法加入RecycledViewPool
                recycleCachedViewAt(0);
                cachedViewSize --;
            }
            if (cachedViewSize < mViewCacheMax) {
                mCachedViews.add(holder);
                cached = true;
            }
        }
        if (!cached) {//未加入mCachedViews時(shí)
            addViewHolderToRecycledViewPool(holder);
            recycled = true;
        }
    } else if (DEBUG) {
    }
    mViewInfoStore.removeViewHolder(holder);
    if (!cached && !recycled && transientStatePreventsRecycling) {
        holder.mOwnerRecyclerView = null;
    }
}

待加入的ViewHolder不能是Scrap酗钞,前面經(jīng)過(guò)unScrap方法處理過(guò)腹忽。緩存mCachedViews最大值是mViewCacheMax,當(dāng)達(dá)到最大時(shí)砚作,刪除第一個(gè)窘奏,被刪除元素加入RecycledViewPool。如果數(shù)量已經(jīng)小于最大值葫录,將新ViewHolder放入mCachedViews緩存着裹,如果仍然大于,將其放入RecycledViewPool压昼。

void addViewHolderToRecycledViewPool(ViewHolder holder) {
    ViewCompat.setAccessibilityDelegate(holder.itemView, null);
    dispatchViewRecycled(holder);//派發(fā)回調(diào)
    holder.mOwnerRecyclerView = null;
    getRecycledViewPool().putRecycledView(holder);//入池
}

將ViewHolder所屬的RecyclerView置空求冷,執(zhí)行dispatchViewRecycled回調(diào),該方法將調(diào)用Adapter的onViewRecycled方法窍霞,可重寫(xiě)。ViewHolder放置到RecycledViewPool緩存池拯坟。

綜上所述

當(dāng)position是0的視圖移除屏幕但金,將ViewHolder存入mCachedViews緩存,最大緩存默認(rèn)是2郁季,當(dāng)position是1的視圖移除屏幕冷溃,也會(huì)存入mCachedViews緩存。當(dāng)position是2的視圖移除屏幕梦裂,將緩存中的第一個(gè)ViewHolder元素刪除似枕,加入RecycledViewPool池。position是2的視圖ViewHolder存入緩存年柠。這是視圖消失的基本邏輯凿歼。


ItemView視圖出現(xiàn)的邏輯

手指觸屏,向上滑動(dòng),position是12,13,14,15...的ItemView依次從底部冒出答憔,通過(guò)調(diào)試源碼味赃,調(diào)用Recycler的getViewForPosition方法。該方法根據(jù)position獲取ItemView視圖虐拓,position是RecyclerView的數(shù)據(jù)源索引心俗,當(dāng)視圖完全展示后,子視圖有12個(gè)蓉驹,那么城榛,最后一個(gè)的索引是11,position是12索引對(duì)應(yīng)視圖不可見(jiàn)态兴,上滑時(shí)狠持,12索引首先出現(xiàn)。

View getViewForPosition(int position, boolean dryRun) {
    /**position邊界判斷**/
    boolean fromScrap = false;
    ViewHolder holder = null;
    if (holder == null) {
        //根據(jù)position從ScrapView中獲取holder
        holder = getScrapViewForPosition(position, INVALID_TYPE, dryRun);
        if (holder != null) {
            //驗(yàn)證holder是否可用于position位置
            if (!validateViewHolderForOffsetPosition(holder)) {
                ...
                holder = null;
            } else {
                fromScrap = true;
            }
        }
    }
    if (holder == null) {
        final int offsetPosition = mAdapterHelper.findPositionOffset(position);
        if (offsetPosition < 0 || offsetPosition >= mAdapter.getItemCount()) {
            //拋出邊界溢出異常IndexOutOfBoundsException
        }
        final int type = mAdapter.getItemViewType(offsetPosition);
        //通過(guò)stable ids查找Scrap
        ...
        if (holder == null) { 
            //從RecycledViewPool獲取
            holder = getRecycledViewPool().getRecycledView(type);
            if (holder != null) {
                holder.resetInternal();//這里會(huì)設(shè)置mPosition=-1
            }
        }
        if (holder == null) {
            //Adapter創(chuàng)建holder 
            holder = mAdapter.createViewHolder(RecyclerView.this, type);
        }
    }
    ...
    boolean bound = false;
    if (mState.isPreLayout() && holder.isBound()) {
    } else if (!holder.isBound() || holder.needsUpdate() || 
                        holder.isInvalid()) {
        final int offsetPosition = mAdapterHelper.findPositionOffset(position);
        holder.mOwnerRecyclerView = RecyclerView.this;
        mAdapter.bindViewHolder(holder, offsetPosition);
        bound = true;
        ...
    }
    ...
    return holder.itemView;
}

首先诗茎,從mAttachedScrap與mCachedViews中查找ViewHolder工坊,在視圖滾動(dòng)時(shí),mAttachedScrap是空的敢订,因此王污,一般情況從mCachedViews緩存查找。
validateViewHolderForOffsetPosition方法楚午,驗(yàn)證holder是否可用于對(duì)應(yīng)position索引昭齐。如果驗(yàn)證通過(guò),設(shè)置fromScrap標(biāo)志矾柜,返回holder的itemView視圖阱驾。如果驗(yàn)證失敗,將增加無(wú)效標(biāo)志怪蔑,holder內(nèi)部mScrapContainer(即Recycler)存在里覆,說(shuō)明holder是isScrap的 ,Scrap的holder無(wú)法被回收缆瓣,unScrap方法提前去除其標(biāo)志喧枷,最后會(huì)加入緩存,recycleViewHolderInternal方法弓坞。
其次隧甚,從RecycledViewPool緩存池中查找。從這里獲取的ViewHolder渡冻,設(shè)置mPosition是NO_POSITION(-1)戚扳。如果都未找到,通過(guò)Adapter的createViewHolder方法創(chuàng)建族吻,調(diào)用Adapter的onCreateViewHolder抽象方法帽借,開(kāi)發(fā)者重寫(xiě)此方法珠增,初始化ItemView,創(chuàng)建ViewHolder對(duì)象宜雀。最后切平,通過(guò)Adapter的bindViewHolder方法,調(diào)用Adapter的onBindViewHolder抽象方法辐董,開(kāi)發(fā)者重寫(xiě)此方法悴品。初始化ViewHolder的View中數(shù)據(jù)。
Recycler的getScrapViewForPosition方法简烘。

ViewHolder getScrapViewForPosition(int position, int type, boolean dryRun) {
    final int scrapCount = mAttachedScrap.size();
    //從mAttachedScrap查找苔严,視圖初始顯示時(shí)走這一步
    //滾動(dòng)時(shí)不會(huì)走這里。
    for (int i = 0; i < scrapCount; i++) {
        final ViewHolder holder = mAttachedScrap.get(i);
        if (!holder.wasReturnedFromScrap() && holder.getLayoutPosition() == position
                && !holder.isInvalid() && (mState.mInPreLayout || !holder.isRemoved())) {
            if (type != INVALID_TYPE && holder.getItemViewType() != type) {
                //ViewType不同
                break;
            }
            holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
            return holder;
        }
    }
    ...
    //從mCachedViews列表查找
    final int cacheSize = mCachedViews.size();
    for (int i = 0; i < cacheSize; i++) {
        final ViewHolder holder = mCachedViews.get(i);
        //無(wú)效標(biāo)志FLAG_INVALID的holder可能存在與cache中孤澎。
        if (!holder.isInvalid() && holder.getLayoutPosition() == position) {
            if (!dryRun) {
                mCachedViews.remove(i);
            }
            return holder;
        }
    }
    return null;
}

當(dāng)視圖滾動(dòng)時(shí)届氢,該方法從緩存mCachedViews查找ViewHolder,并且它的mPosition要和position一致覆旭。
舉個(gè)例子說(shuō)明一下退子。假如position是12完全不可見(jiàn),當(dāng)向上滑動(dòng)時(shí)型将,position是12的視圖出現(xiàn)寂祥,此時(shí),ViewHolder不是getScrapViewForPosition獲取七兜。因?yàn)閙CachedViews還是空丸凭,或者position是0的視圖已在mCachedViews緩存,但它的mPosition是0腕铸,與12不相等惜犀,也不會(huì)使用它。因此狠裹,position是12的要新建ViewHolder虽界。當(dāng)position是13和14...視圖出現(xiàn),對(duì)應(yīng)position是1,2..的視圖要進(jìn)入mCachedViews緩存涛菠,如果mCachedViews緩存未達(dá)到最大值浓恳,將會(huì)一直新建ViewHolder,原因也一樣碗暗,mPosition不符合。如果到達(dá)最大值梢夯,緩存的最大值默認(rèn)是2言疗,此時(shí),已經(jīng)存儲(chǔ)position是0和1的值颂砸,繼續(xù)上滑噪奄,position是2的視圖要進(jìn)緩存死姚,刪掉最早position是0的值,將它放入RecycledViewPool池勤篮。繼續(xù)都毒,position是14的出現(xiàn),從緩存未找到符合的position碰缔,因?yàn)榇丝叹彺胬镞€都是頭部position較小的值账劲,RecycledViewPool已經(jīng)有值,就從RecycledViewPool獲取金抡。這里獲取的與positon無(wú)關(guān)瀑焦,ViewHoder的mPosition都是-1,只要type類(lèi)型一樣梗肝,在Adapter的bindViewHolder方法榛瓮,會(huì)為mPosition賦值,這個(gè)ViewHolder內(nèi)部mPosition就屬于14啦巫击。
改變方向手指下滑禀晓,position是2的視圖出屏幕,對(duì)應(yīng)的ViewHolder在緩存坝锰,直接使用粹懒。position是14的消失了,將position是14的ViewHolder加入緩存什黑。

綜上所述

緩存mCachedViews崎淳,存儲(chǔ)的總是最新消失Item視圖對(duì)應(yīng)的ViewHolder,ype != INVALID_TYPE && holder.getItemVie不管它是在頂部消失愕把,還是在底部消失拣凹。它的最大值也不宜過(guò)大,設(shè)計(jì)過(guò)大的話會(huì)就可以一直裝入恨豁,未出現(xiàn)過(guò)的position都要新建ViewHolder嚣镜。比如,緩存無(wú)限大橘蜜,一屏顯示11個(gè)菊匿,上滑,這11個(gè)都可以進(jìn)入緩存计福,那么后面出來(lái)11個(gè)左右都因position不符而新建跌捆。再下滑,后面出來(lái)的這些也可以進(jìn)入緩存象颖,從緩存取出上面的一批顯示佩厚,這就用不到RecycledViewPool了,失去了它原有的功能说订。
那么抄瓦,為什么會(huì)有mCachedViews呢潮瓶?
如果直接在RecycledViewPool池存儲(chǔ),當(dāng)?shù)撞恳晥D出來(lái)就可以重用第一個(gè)消失的視圖钙姊。對(duì)于在一個(gè)位置不停上下滑動(dòng)時(shí)毯辅,個(gè)人感覺(jué),從mCachedViews查找更快一些煞额。

到這里思恐,我們已經(jīng)獲取了屏幕下一個(gè)將要顯示的ItemView,接下來(lái)就要將它加入到RecyclerView視圖中立镶,調(diào)用LayoutManager#addViewInt方法壁袄。

private void addViewInt(View child, int index, boolean disappearing) {
    final ViewHolder holder = getChildViewHolderInt(child);
    ...
    final LayoutParams lp = (LayoutParams) child.getLayoutParams();
    if (holder.wasReturnedFromScrap() || holder.isScrap()) {
        //視圖剛展現(xiàn)時(shí),從mAttachedScrap獲取數(shù)據(jù)時(shí)觸發(fā)這里媚媒。
        if (holder.isScrap()) {
            holder.unScrap();
        } else {
            holder.clearReturnedFromScrapFlag();
        }
        mChildHelper.attachViewToParent(child, index, child.getLayoutParams(), false);
        if (DISPATCH_TEMP_DETACH) {
            ViewCompat.dispatchFinishTemporaryDetach(child);
        }
    } else if (child.getParent() == mRecyclerView) { 
        int currentIndex = mChildHelper.indexOfChild(child);
        if (index == -1) {
            index = mChildHelper.getChildCount();
        }
        if (currentIndex == -1) {
            //拋出異常
        }
        if (currentIndex != index) {
            mRecyclerView.mLayout.moveView(currentIndex, index);
        }
    } else {
        mChildHelper.addView(child, index, false);
        lp.mInsetsDirty = true;
        if (mSmoothScroller != null && mSmoothScroller.isRunning()) {
            mSmoothScroller.onChildAttachedToWindow(child);
        }
    }
    ....
}

如果發(fā)現(xiàn)ViewHolder的FLAG_RETURNED_FROM_SCRAP標(biāo)志或isScrap嗜逻,先unScrap處理,再調(diào)用ViewGroup的attachViewToParent方法缭召。在滾動(dòng)時(shí)栈顷,獲取的isScrap是false。
借助ChildHelper的addView方法嵌巷,調(diào)用CallBack的addView方法萄凤,最終,調(diào)用的是ViewGroup的addView搪哪,ItemView加入父容器靡努,dispatchChildAttached方法,會(huì)觸發(fā)Adapter的onViewAttachedToWindow方法晓折。

@Override
public void addView(View child, int index) {
    RecyclerView.this.addView(child, index);
    dispatchChildAttached(child);
}
綜上所述

當(dāng)視圖進(jìn)入可視范圍惑朦,從緩存mCachedViews或RecycledViewPool獲取ViewHolder,獲取內(nèi)部ItemView漓概,ViewGroup的addView方法將視圖加入父視圖漾月。
這是視圖可視加/取的邏輯。


ChildHelper輔助類(lèi)

ItemView幫助類(lèi)胃珍,它通過(guò)內(nèi)部Callback接口暴露出來(lái)梁肿,在RecyclerView類(lèi)初始化ChildHelper時(shí)實(shí)現(xiàn)接口方法,調(diào)用RecyclerView的對(duì)應(yīng)方法觅彰。處理子視圖會(huì)借助父類(lèi)ViewGroup吩蔑。

RecyclerView的ChildHelper輔助類(lèi).jpg

任重而道遠(yuǎn)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市填抬,隨后出現(xiàn)的幾起案子哥纫,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,884評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蛀骇,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡读拆,警方通過(guò)查閱死者的電腦和手機(jī)擅憔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)檐晕,“玉大人暑诸,你說(shuō)我怎么就攤上這事”倩遥” “怎么了个榕?”我有些...
    開(kāi)封第一講書(shū)人閱讀 158,369評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)芥喇。 經(jīng)常有香客問(wèn)我西采,道長(zhǎng),這世上最難降的妖魔是什么继控? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,799評(píng)論 1 285
  • 正文 為了忘掉前任械馆,我火速辦了婚禮,結(jié)果婚禮上武通,老公的妹妹穿的比我還像新娘霹崎。我一直安慰自己,他們只是感情好冶忱,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,910評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布尾菇。 她就那樣靜靜地躺著,像睡著了一般囚枪。 火紅的嫁衣襯著肌膚如雪派诬。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 50,096評(píng)論 1 291
  • 那天眶拉,我揣著相機(jī)與錄音千埃,去河邊找鬼。 笑死忆植,一個(gè)胖子當(dāng)著我的面吹牛放可,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播朝刊,決...
    沈念sama閱讀 39,159評(píng)論 3 411
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼耀里,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了拾氓?” 一聲冷哼從身側(cè)響起冯挎,我...
    開(kāi)封第一講書(shū)人閱讀 37,917評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎咙鞍,沒(méi)想到半個(gè)月后房官,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體趾徽,經(jīng)...
    沈念sama閱讀 44,360評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,673評(píng)論 2 327
  • 正文 我和宋清朗相戀三年翰守,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了孵奶。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,814評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蜡峰,死狀恐怖了袁,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情湿颅,我是刑警寧澤载绿,帶...
    沈念sama閱讀 34,509評(píng)論 4 334
  • 正文 年R本政府宣布,位于F島的核電站油航,受9級(jí)特大地震影響崭庸,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜劝堪,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,156評(píng)論 3 317
  • 文/蒙蒙 一冀自、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧秒啦,春花似錦熬粗、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至芳来,卻和暖如春含末,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背即舌。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,123評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工佣盒, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人顽聂。 一個(gè)月前我還...
    沈念sama閱讀 46,641評(píng)論 2 362
  • 正文 我出身青樓肥惭,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親紊搪。 傳聞我的和親對(duì)象是個(gè)殘疾皇子蜜葱,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,728評(píng)論 2 351

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

  • 這篇文章分三個(gè)部分,簡(jiǎn)單跟大家講一下 RecyclerView 的常用方法與奇葩用法耀石;工作原理與ListView比...
    LucasAdam閱讀 4,381評(píng)論 0 27
  • 簡(jiǎn)介: 提供一個(gè)讓有限的窗口變成一個(gè)大數(shù)據(jù)集的靈活視圖牵囤。 術(shù)語(yǔ)表: Adapter:RecyclerView的子類(lèi)...
    酷泡泡閱讀 5,154評(píng)論 0 16
  • 英文注釋 大意如下: 一個(gè)Recycler 負(fù)責(zé)管理 廢棄的和獨(dú)立的 itemview 用于重用。 一個(gè)"scra...
    秋蘭兮青青閱讀 6,840評(píng)論 5 17
  • 【Android 控件 RecyclerView】 概述 RecyclerView是什么 從Android 5.0...
    Rtia閱讀 307,447評(píng)論 27 439
  • 文/小叮當(dāng) 房間狹小 竟容不下三兩個(gè)轉(zhuǎn)身 走進(jìn)院子 任爾奔騰跳躍 天際的浮云總是那一塊 事實(shí)是逃不過(guò)狹隘的景色 走...
    羅布泊的小哆啦閱讀 227評(píng)論 0 0