基于滑動(dòng)場景解析RecyclerView的回收復(fù)用機(jī)制原理

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

最近在研究 RecyclerView 的回收復(fù)用機(jī)制眯娱,順便記錄一下。我們知道士飒,RecyclerView 在 layout 子 View 時(shí)宽档,都通過回收復(fù)用機(jī)制來管理票渠。網(wǎng)上關(guān)于回收復(fù)用機(jī)制的分析講解的文章也有一大堆了,分析得也都很詳細(xì)阱洪,什么四級(jí)緩存啊便贵,先去 mChangedScrap 取再去哪里取啊之類的;但其實(shí)冗荸,我想說的是承璃,RecyclerView 的回收復(fù)用機(jī)制確實(shí)很完善,覆蓋到各種場景中蚌本,但并不是每種場景的回收復(fù)用時(shí)都會(huì)將機(jī)制的所有流程走一遍的盔粹。舉個(gè)例子說,在 setLayoutManager程癌、setAdapter舷嗡、notifyDataSetChanged 或者滑動(dòng)時(shí)等等這些場景都會(huì)觸發(fā)回收復(fù)用機(jī)制的工作。但是如果只是 RecyclerView 滑動(dòng)的場景觸發(fā)的回收復(fù)用機(jī)制工作時(shí)嵌莉,其實(shí)并不需要四級(jí)緩存都參與的进萄。

emmm,應(yīng)該講得還是有點(diǎn)懵锐峭,那就繼續(xù)看下去吧中鼠,會(huì)一點(diǎn)一點(diǎn)慢慢分析。本篇不會(huì)像其他大神的文章一樣沿癞,把回收復(fù)用機(jī)制源碼一行行分析下來援雇,我也沒那個(gè)能力,所以我會(huì)基于一種特定的場景來分析源碼椎扬,這樣會(huì)更容易理解的惫搏。廢話結(jié)束曙旭,開始正題。

正題

RecyclerView 的回收復(fù)用機(jī)制的內(nèi)部實(shí)現(xiàn)都是由 Recycler 內(nèi)部類實(shí)現(xiàn)晶府,下面就都以這樣一種頁面的滑動(dòng)場景來講解 RecyclerView 的回收復(fù)用機(jī)制桂躏。

RecyclerView頁面.png

相應(yīng)的版本:
RecyclerView: recyclerview-v7-25.1.0.jar
LayoutManager: GridLayoutManager extends LinearLayoutManager (recyclerview-v7-25.1.0.jar)

這個(gè)頁面每行可顯示5個(gè)卡位,每個(gè)卡位的 item 布局 type 一致川陆。

開始分析回收復(fù)用機(jī)制之前剂习,先提幾個(gè)問題:

Q1:如果向下滑動(dòng),新一行的5個(gè)卡位的顯示會(huì)去復(fù)用緩存的 ViewHolder较沪,第一行的5個(gè)卡位會(huì)移出屏幕被回收鳞绕,那么在這個(gè)過程中,是先進(jìn)行復(fù)用再回收尸曼?還是先回收再復(fù)用们何?還是邊回收邊復(fù)用?也就是說控轿,新一行的5個(gè)卡位復(fù)用的 ViewHolder 有可能是第一行被回收的5個(gè)卡位嗎冤竹?

第二個(gè)問題之前,先看幾張圖片:

先向下再向上滑動(dòng).png

黑框表示屏幕茬射,RecyclerView 先向下滑動(dòng)鹦蠕,第三行卡位顯示出來,再向上滑動(dòng)在抛,第三行移出屏幕钟病,第一行顯示出來。我們分別在 Adapter 的 onCreateViewHolder() 和 onBindViewHolder() 里打日志刚梭,下面是這個(gè)過程的日志:

日志.png

紅框1是 RecyclerView 向下滑動(dòng)操作的日志肠阱,第三行5個(gè)卡位的顯示都是重新創(chuàng)建的 ViewHolder ;紅框2是再次向上滑動(dòng)時(shí)的日志朴读,第一行5個(gè)卡位的重新顯示用的 ViewHolder 都是復(fù)用的屹徘,因?yàn)闆]有 create viewHolder 的日志,然后只有后面3個(gè)卡位重新綁定數(shù)據(jù)磨德,調(diào)用了onBindViewHolder()缘回;那么問題來了:

Q2: 在這個(gè)過程中,為什么當(dāng) RecyclerView 再次向上滑動(dòng)重新顯示第一行的5個(gè)卡位時(shí)典挑,只有后面3個(gè)卡位觸發(fā)了 onBindViewHolder() 方法酥宴,重新綁定數(shù)據(jù)呢?明明5個(gè)卡位都是復(fù)用的您觉。

在上面的操作基礎(chǔ)上拙寡,我們繼續(xù)往下操作:

先向下再向下.png

在第二個(gè)問題操作的基礎(chǔ)上,目前已經(jīng)創(chuàng)建了15個(gè) ViewHolder琳水,此時(shí)顯示的是第1肆糕、2行的卡位般堆,那么繼續(xù)向下滑動(dòng)兩次,這個(gè)過程的日志如下:

日志.png

紅框1是第二個(gè)問題操作的日志诚啃,在這里截出來只是為了顯示接下去的日志是在上面的基礎(chǔ)上繼續(xù)操作的淮摔;

紅框2就是第一次向下滑時(shí)的日志,對(duì)比問題2的日志始赎,這次第三行的5個(gè)卡位用的 ViewHolder 也都是復(fù)用的和橙,而且也只有后面3個(gè)卡位觸發(fā)了 onBindViewHolder() 重新綁定數(shù)據(jù);

紅框3是第二次向下滑動(dòng)時(shí)的日志造垛,這次第四行的5個(gè)卡位魔招,前3個(gè)的卡位用的 ViewHolder 是復(fù)用的,后面2個(gè)卡位的 ViewHolder 則是重新創(chuàng)建的五辽,而且5個(gè)卡位都調(diào)用了 onBindViewHolder() 重新綁定數(shù)據(jù)办斑;

那么,

Q3:接下去不管是向上滑動(dòng)還是向下滑動(dòng)杆逗,滑動(dòng)幾次乡翅,都不會(huì)再有 onCreateViewHolder() 的日志了,也就是說 RecyclerView 總共創(chuàng)建了17個(gè) ViewHolder髓迎,但有時(shí)一行的5個(gè)卡位只有3個(gè)卡位需要重新綁定數(shù)據(jù)峦朗,有時(shí)卻又5個(gè)卡位都需要重新綁定數(shù)據(jù),這是為什么呢排龄?

如果明白 RecyclerView 的回收復(fù)用機(jī)制,那么這三個(gè)問題也就都知道原因了翎朱;反過來橄维,如果知道這三個(gè)問題的原因,那么理解 RecyclerView 的回收復(fù)用機(jī)制也就更簡單了拴曲;所以争舞,帶著問題,在特定的場景下去分析源碼的話澈灼,應(yīng)該會(huì)比較容易竞川。

源碼分析

其實(shí),根據(jù)問題2的日志叁熔,我們就可以回答問題1了委乌。在目前顯示1、2行荣回,
ViewHolder 的個(gè)數(shù)為10個(gè)的基礎(chǔ)上遭贸,第三行的5個(gè)新卡位要顯示出來都需要重新創(chuàng)建 ViewHolder,也就是說心软,在這個(gè)向下滑動(dòng)的過程壕吹,是5個(gè)新卡位的復(fù)用機(jī)制先進(jìn)行工作著蛙,然后第1行的5個(gè)被移出屏幕的卡位再進(jìn)行回收機(jī)制工作。

那么耳贬,就先來看看復(fù)用機(jī)制的源碼

復(fù)用機(jī)制

getViewForPosition()

Recycler.getViewForPosition()
Recycler.getViewForPosition()
Recycler.tryGetViewHolderForPositionByDeadline

這個(gè)方法是復(fù)用機(jī)制的入口踏堡,也就是 Recycler 開放給外部使用復(fù)用機(jī)制的api,外部調(diào)用這個(gè)方法就可以返回想要的 View咒劲,而至于這個(gè) View 是復(fù)用而來的暂吉,還是重新創(chuàng)建得來的,就都由 Recycler 內(nèi)部實(shí)現(xiàn)缎患,對(duì)外隱藏慕的。

tryGetViewHolderForPositionByDeadline()

所以,Recycler 的復(fù)用機(jī)制內(nèi)部實(shí)現(xiàn)就在這個(gè)方法里挤渔。
分析邏輯之前肮街,先看一下 Recycler 的幾個(gè)結(jié)構(gòu)體,用來緩存 ViewHolder 的判导。

Recycler

mAttachedScrap: 用于緩存顯示在屏幕上的 item 的 ViewHolder嫉父,場景好像是 RecyclerView 在 onLayout 時(shí)會(huì)先把 children 都移除掉,再重新添加進(jìn)去眼刃,所以這個(gè) List 應(yīng)該是用在布局過程中臨時(shí)存放 children 的绕辖,反正在 RecyclerView 滑動(dòng)過程中不會(huì)在這里面來找復(fù)用的 ViewHolder 就是了。

mChangedScrap: 這個(gè)沒理解是干嘛用的擂红,看名字應(yīng)該跟 ViewHolder 的數(shù)據(jù)發(fā)生變化時(shí)有關(guān)吧仪际,在 RecyclerView 滑動(dòng)的過程中,也沒有發(fā)現(xiàn)到這里找復(fù)用的 ViewHolder昵骤,所以這個(gè)可以先暫時(shí)放一邊树碱。

mCachedViews:這個(gè)就重要得多了,滑動(dòng)過程中的回收和復(fù)用都是先處理的這個(gè) List变秦,這個(gè)集合里存的 ViewHolder 的原本數(shù)據(jù)信息都在成榜,所以可以直接添加到 RecyclerView 中顯示,不需要再次重新 onBindViewHolder()蹦玫。

mUnmodifiableAttachedScrap: 不清楚干嘛用的赎婚,暫時(shí)跳過。

mRecyclerPool:這個(gè)也很重要樱溉,但存在這里的 ViewHolder 的數(shù)據(jù)信息會(huì)被重置掉挣输,相當(dāng)于 ViewHolder 是一個(gè)重創(chuàng)新建的一樣,所以需要重新調(diào)用 onBindViewHolder 來綁定數(shù)據(jù)饺窿。

mViewCacheExtension:這個(gè)是留給我們自己擴(kuò)展的歧焦,好像也沒怎么用,就暫時(shí)不分析了。

那么接下去就看看復(fù)用的邏輯:

第1步

第一步很簡單绢馍,position 如果在 item 的范圍之外的話向瓷,那就拋異常吧。繼續(xù)往下看

第2步

如果是在 isPreLayout() 時(shí)舰涌,那么就去 mChangedScrap 中找猖任。
那么這個(gè) isPreLayout 表示的是什么?瓷耙,有兩個(gè)賦值的地方朱躺。

延伸
延伸

emmm,看樣子搁痛,在 LayoutManager 的 onLayoutChildren 前就會(huì)置為
false长搀,不過我還是不懂這個(gè)過程是干嘛的,滑動(dòng)過程中好像
mState.mInPreLayou = false鸡典,所以并不會(huì)來這里源请,先暫時(shí)跳過。繼續(xù)往下彻况。

第3步

跟進(jìn)這個(gè)方法看看

第3.1步 getScrapOrHiddenOrCachedHolderForPosition()

首先谁尸,去 mAttachedScrap 中尋找 position 一致的 viewHolder,需要匹配一些條件纽甘,大致是這個(gè) viewHolder 沒有被移除良蛮,是有效的之類的條件,滿足就返回這個(gè) viewHolder悍赢。

所以决瞳,這里的關(guān)鍵就是要理解這個(gè) mAttachedScrap 到底是什么,存的是哪些 ViewHolder泽裳。

一次遙控器按鍵的操作瞒斩,不管有沒有發(fā)生滑動(dòng),都會(huì)導(dǎo)致 RecyclerView 的重新 onLayout涮总,那要 layout 的話,RecyclerView 會(huì)先把所有 children 先 remove 掉祷舀,然后再重新 add 上去瀑梗,完成一次 layout 的過程。那么這暫時(shí)性的 remove 掉的 viewHolder 要存放在哪呢裳扯,就是放在這個(gè) mAttachedScrap 中了抛丽,這就是我的理解了。

所以饰豺,感覺這個(gè) mAttachedScrap 中存放的 viewHolder 跟回收和復(fù)用關(guān)系不大亿鲜。

網(wǎng)上一些分析的文章有說,RecyclerView 在復(fù)用時(shí)會(huì)按順序去 mChangedScrap, mAttachedScrap 等等緩存里找,沒有找到再往下去找蒿柳,從代碼上來看是這樣沒錯(cuò)饶套,但我覺得這樣表述有問題。因?yàn)榫臀覀冞@篇文章基于 RecyclerView 的滑動(dòng)場景來說垒探,新卡位的復(fù)用以及舊卡位的回收機(jī)制妓蛮,其實(shí)都不會(huì)涉及到mChangedScrap 和 mAttachedScrap,所以我覺得還是基于某種場景來分析相對(duì)應(yīng)的回收復(fù)用機(jī)制會(huì)比較好圾叼。就像mChangedScrap 我雖然沒理解是干嘛用的蛤克,但我猜測應(yīng)該是在當(dāng)數(shù)據(jù)發(fā)生變化時(shí)才會(huì)涉及到的復(fù)用場景,所以當(dāng)我分析基于滑動(dòng)場景時(shí)的復(fù)用時(shí)夷蚊,即使我對(duì)這塊不理解构挤,影響也不會(huì)很大。

繼續(xù)往下看

第3.2步

emmm惕鼓,這段也還是沒看懂筋现,但估計(jì)應(yīng)該需要一些特定的場景下所使用的復(fù)用策略吧,看名字呜笑,應(yīng)該跟 hidden 有關(guān)夫否?不懂,跳過這段叫胁,應(yīng)該也沒事凰慈,滑動(dòng)過程中的回收復(fù)用跟這個(gè)應(yīng)該也關(guān)系不大。

第3.3步

這里就要畫重點(diǎn)啦驼鹅,記筆記記筆記微谓,滑動(dòng)場景中的復(fù)用會(huì)用到這里的機(jī)制。

mCachedViews 的大小默認(rèn)為2输钩。遍歷 mCachedViews豺型,找到 position 一致的 ViewHolder,之前說過买乃,mCachedViews 里存放的 ViewHolder 的數(shù)據(jù)信息都保存著姻氨,所以 mCachedViews 可以理解成,只有原來的卡位可以重新復(fù)用這個(gè) ViewHolder剪验,新位置的卡位無法從 mCachedViews 里拿 ViewHolder出來用肴焊。

找到 viewholder 后

第4步

就算 position 匹配找到了 ViewHolder,還需要判斷一下這個(gè) ViewHolder 是否已經(jīng)被 remove 掉功戚,type 類型一致不一致娶眷,如下。

第4.1步

以上是在 mCachedViews 中尋找啸臀,沒有找到的話届宠,就繼續(xù)再找一遍,剛才是通過 position 來找,那這次就換成id豌注,然后重復(fù)上面的步驟再找一遍伤塌,如下

第5步

getScrapOrCachedViewForId() 做的事跟 getScrapOrHiddenOrCacheHolderForPosition() 其實(shí)差不多,只不過一個(gè)是通過 position 來找 ViewHolder幌羞,一個(gè)是通過 id 來找寸谜。而這個(gè) id 并不是我們?cè)?xml 中設(shè)置的 android:id, 而是 Adapter 持有的一個(gè)屬性属桦,默認(rèn)是不會(huì)使用這個(gè)屬性的熊痴,所以這個(gè)第5步其實(shí)是不會(huì)執(zhí)行的,除非我們重寫了 Adapter 的 setHasStableIds()聂宾,既然不是常用的場景果善,那就先略過吧,那就繼續(xù)往下系谐。

第6步

這個(gè)就是常說擴(kuò)展類了巾陕,RecyclerView 提供給我們自定義實(shí)現(xiàn)的擴(kuò)展類,我們可以重寫 getViewForPositionAndType() 方法來實(shí)現(xiàn)自己的復(fù)用策略纪他。不過鄙煤,也沒用過,那這部分也當(dāng)作不會(huì)執(zhí)行茶袒,略過梯刚。繼續(xù)往下

第7步

這里也是重點(diǎn)了,記筆記記筆記薪寓。

這里是去 RecyclerViewPool 里取 ViewHolder亡资,ViewPool 會(huì)根據(jù)不同的 item type 創(chuàng)建不同的 List,每個(gè) List 默認(rèn)大小為5個(gè)向叉∽赌澹看一下去 ViewPool 里是怎么找的

第7.1步

之前說過,ViewPool 會(huì)根據(jù)不同的 viewType 創(chuàng)建不同的集合來存放 ViewHolder母谎,那么復(fù)用的時(shí)候瘦黑,只要 ViewPool 里相同的 type 有 ViewHolder 緩存的話兔港,就將最后一個(gè)拿出來復(fù)用浙炼,不用像 mCachedViews 需要各種匹配條件,只要有就可以復(fù)用汽煮。

繼續(xù)看"圖第7步"后面的代碼冻记,拿到 ViewHolder 之后,還會(huì)再次調(diào)用 resetInternal() 來重置 ViewHolder来惧,這樣 ViewHolder 就可以當(dāng)作一個(gè)全新的 ViewHolder 來使用了冗栗,這也就是為什么從這里拿的 ViewHolder 都需要重新 onBindViewHolder() 了

那如果在 ViewPool 里還是沒有找到呢,繼續(xù)往下看

第8步

如果 ViewPool 中都沒有找到 ViewHolder 來使用的話隅居,那就調(diào)用 Adapter 的 onCreateViewHolder 來創(chuàng)建一個(gè)新的 ViewHolder 使用钠至。

上面一共有很多步驟來找 ViewHolder,不管在哪個(gè)步驟胎源,只要找到 ViewHolder 的話棉钧,那下面那些步驟就不用管了,然后都要繼續(xù)往下判斷是否需要重新綁定數(shù)據(jù)涕蚤,還有檢查布局參數(shù)是否合法宪卿。如下:

最后1步

到這里,tryGetViewHolderForPositionByDeadline() 這個(gè)方法就結(jié)束了万栅。這大概就是 RecyclerView 的復(fù)用機(jī)制佑钾,中間我們跳過很多地方,因?yàn)?RecyclerView 有各種場景可以刷新他的 view烦粒,比如重新 setLayoutManager()休溶,重新 setAdapter(),或者 notifyDataSetChanged()扰她,或者滑動(dòng)等等之類的場景兽掰,只要重新layout,就會(huì)去回收和復(fù)用 ViewHolder徒役,所以這個(gè)復(fù)用機(jī)制需要考慮到各種各樣的場景孽尽。

把代碼一行行的啃透有點(diǎn)吃力,所以我就只借助 RecyclerView 的滑動(dòng)的這種場景來分析它涉及到的回收和復(fù)用機(jī)制廉涕。

下面就分析一下回收機(jī)制

回收機(jī)制

回收機(jī)制的入口就有很多了泻云,因?yàn)?Recycler 有各種結(jié)構(gòu)體,比如mAttachedScrap狐蜕,mCachedViews 等等宠纯,不同結(jié)構(gòu)體回收的時(shí)機(jī)都不一樣,入口也就多了层释。

所以婆瓜,還是基于 RecyclerView 的滑動(dòng)場景下,移出屏幕的卡位回收時(shí)的入口是:

Recycler.recyclerView()

本篇分析的滑動(dòng)場景贡羔,在 RecyclerView 滑動(dòng)時(shí)廉白,會(huì)交由 LinearLayoutManager 的 scrollVerticallyBy() 去處理,然后 LayoutManager 會(huì)接著調(diào)用 fill() 方法去處理需要復(fù)用和回收的卡位乖寒,最終會(huì)調(diào)用上述 recyclerView() 這個(gè)方法開始進(jìn)行回收工作猴蹂。

recycleViewHolderInternal()
上圖第一個(gè)紅框中的代碼
從 mCacheViews 中扔 ViewHolder 到 ViewPool中去
addViewHolderToRecycledViewPool
putRecycledView()

回收的邏輯比較簡單,由 LayoutManager 來遍歷移出屏幕的卡位楣嘁,然后對(duì)每個(gè)卡位進(jìn)行回收操作磅轻,回收時(shí)珍逸,都是把 ViewHolder 放在 mCachedViews 里面,如果 mCachedViews 滿了聋溜,那就在 mCachedViews 里拿一個(gè) ViewHolder 扔到 ViewPool 緩存里谆膳,然后 mCachedViews 就可以空出位置來放新回收的 ViewHolder 了。

總結(jié)一下:

RecyclerView 滑動(dòng)場景下的回收復(fù)用涉及到的結(jié)構(gòu)體兩個(gè):
mCachedViews 和 RecyclerViewPool

mCachedViews 優(yōu)先級(jí)高于 RecyclerViewPool撮躁,回收時(shí)漱病,最新的 ViewHolder 都是往 mCachedViews 里放,如果它滿了把曼,那就移出一個(gè)扔到 ViewPool 里好空出位置來緩存最新的 ViewHolder杨帽。

復(fù)用時(shí),也是先到 mCachedViews 里找 ViewHolder祝迂,但需要各種匹配條件睦尽,概括一下就是只有原來位置的卡位可以復(fù)用存在 mCachedViews 里的 ViewHolder,如果 mCachedViews 里沒有型雳,那么才去 ViewPool 里找当凡。

在 ViewPool 里的 ViewHolder 都是跟全新的 ViewHolder 一樣,只要 type 一樣纠俭,有找到沿量,就可以拿出來復(fù)用,重新綁定下數(shù)據(jù)即可冤荆。

整體的流程圖如下:(可放大查看)

滑動(dòng)場景下的回收復(fù)用流程圖.png

最后朴则,解釋一下開頭的問題

Q1:如果向下滑動(dòng),新一行的5個(gè)卡位的顯示會(huì)去復(fù)用緩存的 ViewHolder钓简,第一行的5個(gè)卡位會(huì)移出屏幕被回收乌妒,那么在這個(gè)過程中,是先進(jìn)行復(fù)用再回收外邓?還是先回收再復(fù)用撤蚊?還是邊回收邊復(fù)用?也就是說损话,新一行的5個(gè)卡位復(fù)用的 ViewHolder 有可能是第一行被回收的5個(gè)卡位嗎侦啸?

答:先復(fù)用再回收,新一行的5個(gè)卡位先去目前的 mCachedViews 和 ViewPool 的緩存中尋找復(fù)用丧枪,沒有就重新創(chuàng)建光涂,然后移出屏幕的那行的5個(gè)卡位再回收緩存到 mCachedViews 和 ViewPool 里面,所以新一行5個(gè)卡位和復(fù)用不可能會(huì)用到剛移出屏幕的5個(gè)卡位拧烦。

Q2: 在這個(gè)過程中忘闻,為什么當(dāng) RecyclerView 再次向上滑動(dòng)重新顯示第一行的5個(gè)卡位時(shí),只有后面3個(gè)卡位觸發(fā)了 onBindViewHolder() 方法恋博,重新綁定數(shù)據(jù)呢服赎?明明5個(gè)卡位都是復(fù)用的葵蒂。

答:滑動(dòng)場景下涉及到的回收和復(fù)用的結(jié)構(gòu)體是 mCachedViews 和 ViewPool,前者默認(rèn)大小為2重虑,后者為5。所以秦士,當(dāng)?shù)谌酗@示出來后缺厉,第一行的5個(gè)卡位被回收,回收時(shí)先緩存在 mCachedViews隧土,滿了再移出舊的到 ViewPool 里提针,所有5個(gè)卡位有2個(gè)緩存在 mCachedViews 里,3個(gè)緩存在 ViewPool曹傀,至于是哪2個(gè)緩存在 mCachedViews辐脖,這是由 LayoutManager 控制。

上面講解的例子使用的是 GridLayoutManager皆愉,滑動(dòng)時(shí)的回收邏輯則是在父類 LinearLayoutManager 里實(shí)現(xiàn)嗜价,回收第一行卡位時(shí)是從后往前回收,所以最新的兩個(gè)卡位是0幕庐、1久锥,會(huì)放在 mCachedViews 里,而2异剥、3瑟由、4的卡位則放在 ViewPool 里。

所以冤寿,當(dāng)再次向上滑動(dòng)時(shí)歹苦,第一行5個(gè)卡位會(huì)去兩個(gè)結(jié)構(gòu)體里找復(fù)用,之前說過督怜,mCachedViews 里存放的 ViewHolder 只有原本位置的卡位才能復(fù)用殴瘦,所以0、1兩個(gè)卡位都可以直接去 mCachedViews 里拿 ViewHolder 復(fù)用亮蛔,而且這里的 ViewHolder 是不用重新綁定數(shù)據(jù)的痴施,至于2、3究流、4卡位則去 ViewPool 里找辣吃,剛好 ViewPool 里緩存著3個(gè) ViewHolder,所以第一行的5個(gè)卡位都是用的復(fù)用的芬探,而從 ViewPool 里拿的復(fù)用需要重新綁定數(shù)據(jù)神得,才會(huì)這樣只有三個(gè)卡位需要重新綁定數(shù)據(jù)。

Q3:接下去不管是向上滑動(dòng)還是向下滑動(dòng)偷仿,滑動(dòng)幾次哩簿,都不會(huì)再有 onCreateViewHolder() 的日志了宵蕉,也就是說 RecyclerView 總共創(chuàng)建了17個(gè) ViewHolder,但有時(shí)一行的5個(gè)卡位只有3個(gè)卡位需要重新綁定數(shù)據(jù)节榜,有時(shí)卻又5個(gè)卡位都需要重新綁定數(shù)據(jù)羡玛,這是為什么呢?

答:有時(shí)一行只有3個(gè)卡位需要重新綁定的原因跟Q2一樣宗苍,因?yàn)?mCachedView 里正好緩存著當(dāng)前位置的 ViewHolder稼稿,本來就是它的 ViewHolder 當(dāng)然可以直接拿來用。而至于為什么會(huì)創(chuàng)建了17個(gè) ViewHolder讳窟,那是因?yàn)樵俚谒男械目ㄎ灰@示出來時(shí)让歼,ViewPool 里只有3個(gè)緩存,而第四行的卡位又用不了 mCachedViews 里的2個(gè)緩存丽啡,因?yàn)檫@兩個(gè)緩存的是0谋右、1卡位的 ViewHolder,所以就需要再重新創(chuàng)建2個(gè) ViewHodler 來給第四行最后的兩個(gè)卡位使用补箍。


QQ圖片20180316094923.jpg

最近剛開通了公眾號(hào)改执,想激勵(lì)自己堅(jiān)持寫作下去,初期主要分享原創(chuàng)的Android或Android-Tv方面的小知識(shí)馏予,感興趣的可以點(diǎn)一波關(guān)注天梧,謝謝支持~~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市霞丧,隨后出現(xiàn)的幾起案子呢岗,更是在濱河造成了極大的恐慌,老刑警劉巖蛹尝,帶你破解...
    沈念sama閱讀 206,839評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件后豫,死亡現(xiàn)場離奇詭異,居然都是意外死亡突那,警方通過查閱死者的電腦和手機(jī)挫酿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來愕难,“玉大人早龟,你說我怎么就攤上這事∶ㄧ裕” “怎么了葱弟?”我有些...
    開封第一講書人閱讀 153,116評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長猜丹。 經(jīng)常有香客問我芝加,道長,這世上最難降的妖魔是什么射窒? 我笑而不...
    開封第一講書人閱讀 55,371評(píng)論 1 279
  • 正文 為了忘掉前任藏杖,我火速辦了婚禮将塑,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘蝌麸。我一直安慰自己点寥,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評(píng)論 5 374
  • 文/花漫 我一把揭開白布祥楣。 她就那樣靜靜地躺著开财,像睡著了一般。 火紅的嫁衣襯著肌膚如雪误褪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,111評(píng)論 1 285
  • 那天碾褂,我揣著相機(jī)與錄音兽间,去河邊找鬼。 笑死正塌,一個(gè)胖子當(dāng)著我的面吹牛嘀略,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播乓诽,決...
    沈念sama閱讀 38,416評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼帜羊,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了鸠天?” 一聲冷哼從身側(cè)響起讼育,我...
    開封第一講書人閱讀 37,053評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎稠集,沒想到半個(gè)月后奶段,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,558評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡剥纷,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評(píng)論 2 325
  • 正文 我和宋清朗相戀三年痹籍,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片晦鞋。...
    茶點(diǎn)故事閱讀 38,117評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蹲缠,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出悠垛,到底是詐尸還是另有隱情线定,我是刑警寧澤,帶...
    沈念sama閱讀 33,756評(píng)論 4 324
  • 正文 年R本政府宣布鼎文,位于F島的核電站渔肩,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏拇惋。R本人自食惡果不足惜周偎,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評(píng)論 3 307
  • 文/蒙蒙 一抹剩、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蓉坎,春花似錦澳眷、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至勿侯,卻和暖如春拓瞪,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背助琐。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評(píng)論 1 262
  • 我被黑心中介騙來泰國打工祭埂, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人兵钮。 一個(gè)月前我還...
    沈念sama閱讀 45,578評(píng)論 2 355
  • 正文 我出身青樓蛆橡,卻偏偏與公主長得像,于是被迫代替她去往敵國和親掘譬。 傳聞我的和親對(duì)象是個(gè)殘疾皇子泰演,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評(píng)論 2 345

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