嗨,大家好,最近我去淘了一些關于RecycleView的面試真題,大家一起看看吧,這次的問題如果都弄懂了,下次面試再遇到RecycleView應該就沒啥可擔心的了,
- 講一下
RecyclerView的快取機制,滑動10個,再滑回去,會有幾個執行onBindView,快取的是什么?cachedView會執行onBindView嗎? RecyclerView預取機制- 如何實作
RecyclerView的區域更新,用過payload嗎,notifyItemChange方法中的引數? RecyclerView嵌套RecyclerView滑動沖突,NestScrollView嵌套RecyclerView,- 說說RecyclerView性能優化,
講一下RecyclerView的快取機制,滑動10個,再滑回去,會有幾個執行onBindView,快取的是什么?cachedView會執行onBindView嗎?
RecyclerView預取機制
這兩個問題都是關于快取的,我就一起說了,
1)首先說下RecycleView的快取結構:
Recycleview有四級快取,分別是mAttachedScrap(螢屏內),mCacheViews(螢屏外),mViewCacheExtension(自定義快取),mRecyclerPool(快取池)
mAttachedScrap(螢屏內),用于螢屏內itemview快速重用,不需要重新createView和bindViewmCacheViews(螢屏外),保存最近移出螢屏的ViewHolder,包含資料和position資訊,復用時必須是相同位置的ViewHolder才能復用,應用場景在那些需要來回滑動的串列中,當往回滑動時,能直接復用ViewHolder資料,不需要重新bindView,mViewCacheExtension(自定義快取),不直接使用,需要用戶自定義實作,默認不實作,mRecyclerPool(快取池),當cacheView滿了后或者adapter被更換,將cacheView中移出的ViewHolder放到Pool中,放之前會把ViewHolder資料清除掉,所以復用時需要重新bindView,
2)四級快取按照順序需要依次讀取,所以完整快取流程是:
- 保存快取流程:
- 插入或是洗掉
itemView時,先把螢屏內的ViewHolder保存至AttachedScrap中 - 滑動螢屏的時候,先消失的itemview會保存到
CacheView,CacheView大小默認是2,超過數量的話按照先入先出原則,移出頭部的itemview保存到RecyclerPool快取池(如果有自定義快取就會保存到自定義快取里),RecyclerPool快取池會按照itemview的itemtype進行保存,每個itemType快取個數為5個,超過就會被回收,
- 獲取快取流程:
- AttachedScrap中獲取,通過pos匹配holder——>獲取失敗,從
CacheView中獲取,也是通過pos獲取holder快取
——>獲取失敗,從自定義快取中獲取快取——>獲取失敗,從mRecyclerPool中獲取
——>獲取失敗,重新創建viewholder——createViewHolder并bindview,
3)了解了快取結構和快取流程,我們再來看看具體的問題
滑動10個,再滑回去,會有幾個執行onBindView?
- 由之前的快取結構可知,需要重新執行
onBindView的只有一種快取區,就是快取池mRecyclerPool,
所以我們假設從加載RecyclView開始盤的話(頁面假設可以容納7條資料):
- 首先,7條資料會依次呼叫
onCreateViewHolder和onBindViewHolder, - 往下滑一條(position=7),那么會把position=0的資料放到
mCacheViews中,此時mCacheViews快取區數量為1,mRecyclerPool數量為0,然后新出現的position=7的資料通過postion在mCacheViews中找不到對應的ViewHolder,通過itemtype也在mRecyclerPool中找不到對應的資料,所以會呼叫onCreateViewHolder和onBindViewHolder方法, - 再往下滑一條資料(position=8),如上,
- 再往下滑一條資料(position=9),position=2的資料會放到
mCacheViews中,但是由于mCacheViews快取區默認容量為2,所以position=0的資料會被清空資料然后放到mRecyclerPool快取池中,而新出現的position=9資料由于在mRecyclerPool中還是找不到相應type的ViewHolder,所以還是會走onCreateViewHolder和onBindViewHolder方法,所以此時mCacheViews快取區數量為2,mRecyclerPool數量為1, - 再往下滑一條資料(position=10),這時候由于可以在
mRecyclerPool中找到相同viewtype的ViewHolder了,所以就直接復用了,并呼叫onBindViewHolder方法系結資料, - 后面依次類推,剛消失的兩條資料會被放到
mCacheViews中,再出現的時候是不會呼叫onBindViewHolder方法,而復用的第三條資料是從mRecyclerPool中取得,就會呼叫onBindViewHolder方法了,
4)所以這個問題就得出結論了(假設mCacheViews容量為默認值2):
-
如果一開始滑動的是新資料,那么滑動10個,就會走10個
bindview方法,然后滑回去,會走10-2個bindview方法,一共18次呼叫, -
如果一開始滑動的是老資料,那么滑動10-2個,就會走8個
bindview方法,然后滑回去,會走10-2個bindview方法,一共16次呼叫,
但是但是,實際情況又有點不一樣,因為Recycleview在v25版本引入了一個新的機制,預取機制,
預取機制,就是在滑動程序中,會把將要展示的一個元素提前快取到mCachedViews中,所以滑動10個元素的時候,第11個元素也會被創建,也就多走了一次bindview方法,但是滑回去的時候不影響,因為就算提前取了一個快取資料,只是把bindview方法提前了,并不影響總的系結item數量,
所以滑動的是新資料的情況下就會多一次呼叫bindview方法,
5)總結,問題怎么答呢?
- 四級快取和流程說一下,
- 滑動10個,再滑回去,
bindview可以是19次呼叫,可以是16次呼叫, - 快取的其實就是快取item的view,在Recycleview中就是
viewholder, cachedView就是mCacheViews快取區中的view,是不需要重新系結資料的,
如何實作RecyclerView的區域更新,用過payload嗎,notifyItemChange方法中的引數?
關于RecycleView的資料更新,主要有以下幾個方法:
notifyDataSetChanged(),重繪全部可見的item,
*notifyItemChanged(int),重繪指定item,notifyItemRangeChanged(int,int),從指定位置開始重繪指定個item,notifyItemInserted(int)、notifyItemMoved(int)、notifyItemRemoved(int),插入、移動一個并自動重繪,notifyItemChanged(int, Object),區域重繪,
可以看到,關于view的區域重繪就是notifyItemChanged(int, Object)方法,下面具體說說:
notifyItemChange有兩個構造方法:
- notifyItemChanged(int position, @Nullable Object payload)
- notifyItemChanged(int position)
其中payload引數可以認為是你要重繪的一個標示,比如我有時候只想重繪itemView中的textview,有時候只想重繪imageview?又或者我只想某一個view的文字顏色進行高亮設定?那么我就可以通過payload引數來標示這個特殊的需求了,
具體怎么做呢?比如我呼叫了notifyItemChanged(14,"changeColor"),那么在onBindViewHolder回呼方法中做下判斷即可:
@Override
public void onBindViewHolder(ViewHolderholder, int position, List<Object> payloads) {
if (payloads.isEmpty()) {
// payloads為空,說明是更新整個ViewHolder
onBindViewHolder(holder, position);
} else {
// payloads不為空,這只更新需要更新的View即可,
String payload = payloads.get(0).toString();
if ("changeColor".equals(payload)) {
holder.textView.setTextColor("");
}
}
}
RecyclerView嵌套RecyclerView滑動沖突,NestScrollView嵌套RecyclerView,
1)RecyclerView嵌套RecyclerView的情況下,如果兩者都要上下滑動,那么就會引起滑動沖突,默認情況下外層的RecycleView可滑,內層不可滑,
之前說過解決滑動沖突的辦法有兩種:內部攔截法和外部攔截法,
這里我提供一種內部攔截法,還有一些其他的辦法大家可以自己思考下,
holder.recyclerView.setOnTouchListener { v, event ->
when(event.action){
//當按下操作的時候,就通知父view不要攔截,拿起操作就設定可以攔截,正常走父view的滑動,
MotionEvent.ACTION_DOWN,MotionEvent.ACTION_MOVE -> v.parent.requestDisallowInterceptTouchEvent(true)
MotionEvent.ACTION_UP -> v.parent.requestDisallowInterceptTouchEvent(false)
}
false}
2)關于ScrclerView的滑動沖突還是同樣的解決辦法,就是進行事件攔截,
還有一個辦法就是用Nestedscrollview代替ScrollView,Nestedscrollview是官方為了解決滑動沖突問題而設計的新的View,它的定義就是支持嵌套滑動的ScrollView,
所以直接替換成Nestedscrollview就能保證兩者都能正常滑動了,但是要注意設定RecyclerView.setNestedScrollingEnabled(false)這個方法,用來取消RecyclerView本身的滑動效果,
這是因為RecyclerView默認是setNestedScrollingEnabled(true),這個方法的含義是支持嵌套滾動的,也就是說當它嵌套在NestedScrollView中時,默認會隨著NestedScrollView滾動而滾動,放棄了自己的滾動,所以給我們的感覺就是滯留、卡頓,所以我們將它設定為false就解決了卡頓問題,讓他正常的滑動,不受外部影響,
說說RecyclerView性能優化,
bindViewHolder方法是在UI執行緒進行的,此方法不能耗時操作,不然將會影響滑動流暢性,比如進行日期的格式化,- 對于新增或洗掉的時候,可以使用
diffutil進行區域重繪,少用全域重繪 - 對于
itemVIew進行布局優化,比如少嵌套等, - 25.1.0 (>=21)及以上使用
Prefetch功能,也就是預取功能,嵌套時且使用的是LinearLayoutManager,子RecyclerView可通過setInitialPrefatchItemCount設定預取個數 - 加大
RecyclerView快取,比如cacheview大小默認為2,可以設定大點,用空間來換取時間,提高流暢度 - 如果高度固定,可以設定
setHasFixedSize(true)來避免requestLayout浪費資源,否則每次更新資料都會重新測量高度,
void onItemsInsertedOrRemoved() {
if (hasFixedSize) layoutChildren();
else requestLayout();
}
- 如果多個
RecycledView的 Adapter 是一樣的,比如嵌套的 RecyclerView 中存在一樣的 Adapter,可以通過設定RecyclerView.setRecycledViewPool(pool);來共用一個RecycledViewPool,這樣就減少了創建VIewholder的開銷, - 在RecyclerView的元素比較高,一屏只能顯示一個元素的時候,第一次滑動到第二個元素會卡頓,這種情況就可以通過設定額外的快取空間,重寫
getExtraLayoutSpace方法即可,
new LinearLayoutManager(this) {
@Override
protected int getExtraLayoutSpace(RecyclerView.State state) {
return size;
}
};
- 設定
RecyclerView.addOnScrollListener();來在滑動程序中停止加載的操作, - 減少物件的創建,比如設定監聽事件,可以全域創建一個,所有view公用一個listener,并且放到
CreateView里面去創建監聽,因為CreateView呼叫要少于bindview,這樣就減少了物件創建所造成的消耗 - 用
notifyDataSetChange時,配接器不知道整個資料集中的那些內容以及存在,再重新匹配ViewHolder時會花生閃爍,設定adapter.setHasStableIds(true),并重寫getItemId()來給每個Item一個唯一的ID,也就是唯一標識,就使itemview的焦點固定,解決了閃爍問題,
拜拜
今天聊了不少,關于RecycleView重要的知識點應該都涉及到了,其中bindview的問題下次有機會我會再詳細的說一下,配合圖片日志,
最后希望大家好好鞏固知識,加油,
拜拜
有一起學習的小伙伴可以關注下??我的公眾號——碼上積木,每天剖析一個知識點,我們一起積累知識,
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/229047.html
標籤:其他
上一篇:Git 的基本使用
下一篇:演算法的時間復雜度

