本篇文章已授權(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ī)制桂躏。
相應(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è)問題之前,先看幾張圖片:
黑框表示屏幕茬射,RecyclerView 先向下滑動(dòng)鹦蠕,第三行卡位顯示出來,再向上滑動(dòng)在抛,第三行移出屏幕钟病,第一行顯示出來。我們分別在 Adapter 的 onCreateViewHolder() 和 onBindViewHolder() 里打日志刚梭,下面是這個(gè)過程的日志:
紅框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ù)往下操作:
在第二個(gè)問題操作的基礎(chǔ)上,目前已經(jīng)創(chuàng)建了15個(gè) ViewHolder琳水,此時(shí)顯示的是第1肆糕、2行的卡位般堆,那么繼續(xù)向下滑動(dòng)兩次,這個(gè)過程的日志如下:
紅框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()
這個(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 的判导。
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ù)用的邏輯:
第一步很簡單绢馍,position 如果在 item 的范圍之外的話向瓷,那就拋異常吧。繼續(xù)往下看
如果是在 isPreLayout() 時(shí)舰涌,那么就去 mChangedScrap 中找猖任。
那么這個(gè) isPreLayout 表示的是什么?瓷耙,有兩個(gè)賦值的地方朱躺。
emmm,看樣子搁痛,在 LayoutManager 的 onLayoutChildren 前就會(huì)置為
false长搀,不過我還是不懂這個(gè)過程是干嘛的,滑動(dòng)過程中好像
mState.mInPreLayou = false鸡典,所以并不會(huì)來這里源请,先暫時(shí)跳過。繼續(xù)往下彻况。
跟進(jìn)這個(gè)方法看看
首先谁尸,去 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ù)往下看
emmm惕鼓,這段也還是沒看懂筋现,但估計(jì)應(yīng)該需要一些特定的場景下所使用的復(fù)用策略吧,看名字呜笑,應(yīng)該跟 hidden 有關(guān)夫否?不懂,跳過這段叫胁,應(yīng)該也沒事凰慈,滑動(dòng)過程中的回收復(fù)用跟這個(gè)應(yīng)該也關(guān)系不大。
這里就要畫重點(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 后
就算 position 匹配找到了 ViewHolder,還需要判斷一下這個(gè) ViewHolder 是否已經(jīng)被 remove 掉功戚,type 類型一致不一致娶眷,如下。
以上是在 mCachedViews 中尋找啸臀,沒有找到的話届宠,就繼續(xù)再找一遍,剛才是通過 position 來找,那這次就換成id豌注,然后重復(fù)上面的步驟再找一遍伤塌,如下
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ù)往下系谐。
這個(gè)就是常說擴(kuò)展類了巾陕,RecyclerView 提供給我們自定義實(shí)現(xiàn)的擴(kuò)展類,我們可以重寫 getViewForPositionAndType() 方法來實(shí)現(xiàn)自己的復(fù)用策略纪他。不過鄙煤,也沒用過,那這部分也當(dāng)作不會(huì)執(zhí)行茶袒,略過梯刚。繼續(xù)往下
這里也是重點(diǎn)了,記筆記記筆記薪寓。
這里是去 RecyclerViewPool 里取 ViewHolder亡资,ViewPool 會(huì)根據(jù)不同的 item type 創(chuàng)建不同的 List,每個(gè) List 默認(rèn)大小為5個(gè)向叉∽赌澹看一下去 ViewPool 里是怎么找的
之前說過,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ù)往下看
如果 ViewPool 中都沒有找到 ViewHolder 來使用的話隅居,那就調(diào)用 Adapter 的 onCreateViewHolder 來創(chuàng)建一個(gè)新的 ViewHolder 使用钠至。
上面一共有很多步驟來找 ViewHolder,不管在哪個(gè)步驟胎源,只要找到 ViewHolder 的話棉钧,那下面那些步驟就不用管了,然后都要繼續(xù)往下判斷是否需要重新綁定數(shù)據(jù)涕蚤,還有檢查布局參數(shù)是否合法宪卿。如下:
到這里,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í)的入口是:
本篇分析的滑動(dòng)場景贡羔,在 RecyclerView 滑動(dòng)時(shí)廉白,會(huì)交由 LinearLayoutManager 的 scrollVerticallyBy() 去處理,然后 LayoutManager 會(huì)接著調(diào)用 fill() 方法去處理需要復(fù)用和回收的卡位乖寒,最終會(huì)調(diào)用上述 recyclerView() 這個(gè)方法開始進(jìn)行回收工作猴蹂。
回收的邏輯比較簡單,由 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ù)即可冤荆。
整體的流程圖如下:(可放大查看)
最后朴则,解釋一下開頭的問題
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è)卡位使用补箍。
最近剛開通了公眾號(hào)改执,想激勵(lì)自己堅(jiān)持寫作下去,初期主要分享原創(chuàng)的Android或Android-Tv方面的小知識(shí)馏予,感興趣的可以點(diǎn)一波關(guān)注天梧,謝謝支持~~