RecyclerView
作為Android開發(fā)中最常用的View之一斜筐。很多App
的feed流都是使用RecyclerView
來實現(xiàn)的顷链。加深對于RecyclerView
的掌握對于開發(fā)效率和開發(fā)質(zhì)量都有很重要的意義屈梁。接下來我打算從源碼
角度剖析RecyclerView
的實現(xiàn),加深對于RecycledView
的了解煞抬。RecyclerView
的源碼實現(xiàn)還是很龐大的革答。本文就先來看一下RecyclerView
的整體設(shè)計曙强,了解其核心實現(xiàn)類的作用以及大致實現(xiàn)原理。
下面這張圖是我截取的RecyclerView的Structure:
本文著重看: ViewHolder
溪食、Adapter
错沃、AdapterDataObservable
、RecyclerViewDataObserver
玉掸、LayoutManager
登疗、、Recycler
断傲、RecyclerPool
认罩。 從而理解RecycledView
的大致實現(xiàn)原理续捂。
先用一張圖大致描述他們之間的關(guān)系,這張圖是adapter.notifyXX()
時RecyclerView
的執(zhí)行邏輯涉及到的一些類:
ViewHolder
對于Adapter
來說牙瓢,一個ViewHolder
就對應(yīng)一個data
。它也是Recycler緩存池
的基本單元页慷。
class ViewHolder {
public final View itemView;
int mPosition = NO_POSITION;
int mItemViewType = INVALID_TYPE;
int mFlags;
...
}
上面我列出了ViewHolder
最重要的4個屬性:
- itemView : 會被當做
child view
來add
到RecyclerView
中酒繁。 - mPosition : 標記當前的
ViewHolder
在Adapter
中所處的位置控妻。 - mItemViewType : 這個
ViewHolder
的Type
弓候,在ViewHolder
保存到RecyclerPool
時,主要靠這個類型來對ViewHolder
做復(fù)用彰居。 - mFlags : 標記
ViewHolder
的狀態(tài)撰筷,比如FLAG_BOUND(顯示在屏幕上)
毕籽、FLAG_INVALID(無效,想要使用必須rebound)
溶握、FLAG_REMOVED(已被移除)
等蒸播。
Adapter
它的工作是把data
和View
綁定,即上面說的一個data
對應(yīng)一個ViewHolder
胀屿。主要負責ViewHolder
的創(chuàng)建以及數(shù)據(jù)變化時通知RecycledView
宿崭。比如下面這個Adapter:
class SimpleStringAdapter(val dataSource: List<String>, val context: Context) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
if (holder.itemView is ViewHolderRenderProtocol) {
(holder.itemView as ViewHolderRenderProtocol).render(dataSource[position], position)
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = SimpleVH(SimpleStringView(context))
override fun getItemCount() = dataSource.size
override fun getItemViewType(position: Int) = 1
override fun notifyDataSetChanged() { //super的實現(xiàn)
mObservable.notifyChanged();
}
}
即:
- 它引用著一個數(shù)據(jù)源集合
dataSource
-
getItemCount()
用來告訴RecyclerView
展示的總條目 - 它并不是直接映射
data -> ViewHolder
葡兑, 而是data position -> data type -> viewholder
赞草。 所以對于ViewHolder
來說,它知道的只是它的view type
AdapterDataObservable
Adapter
是數(shù)據(jù)源的直接接觸者洲守,當數(shù)據(jù)源發(fā)生變化時岖沛,它需要通知給RecyclerView
搭独。這里使用的模式是觀察者模式
。AdapterDataObservable
是數(shù)據(jù)源變化時的被觀察者唉俗。RecyclerViewDataObserver
是觀察者虫溜。
在開發(fā)中我們通常使用adapter.notifyXX()
來刷新UI,實際上Adapter
會調(diào)用AdapterDataObservable
的notifyChanged()
:
public void notifyChanged() {
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onChanged();
}
}
邏輯很簡單股缸,即通知Observer
數(shù)據(jù)發(fā)生變化。
RecyclerViewDataObserver
它是RecycledView
用來監(jiān)聽Adapter
數(shù)據(jù)變化的觀察者:
public void onChanged() {
mState.mStructureChanged = true; // RecycledView每一次UI的更新都會有一個State
processDataSetCompletelyChanged(true);
if (!mAdapterHelper.hasPendingUpdates()) {
requestLayout();
}
}
LayoutManager
它是RecyclerView
的布局管理者瘾境,RecyclerView
在onLayout
時,會利用它來layoutChildren
,它決定了RecyclerView
中的子View的擺放規(guī)則犬绒。但不止如此, 它做的工作還有:
- 測量子View
- 對子View進行布局
- 對子View進行回收
- 子View動畫的調(diào)度
- 負責
RecyclerView
滾動的實現(xiàn) - ...
Recycler
對于LayoutManager
來說兑凿,它是ViewHolder
的提供者。對于RecyclerView
來說咐鹤,它是ViewHolder
的管理者慷暂,是RecyclerView
最核心的實現(xiàn)晨雳。下面這張圖大致描述了它的組成:
scrap list
final ArrayList<ViewHolder> mAttachedScrap = new ArrayList<>();
ArrayList<ViewHolder> mChangedScrap = null;
View Scrap狀態(tài)
相信你在許多RecyclerView
的crash log
中都看到過這個單詞。它是指View
在RecyclerView
布局期間進入分離狀態(tài)的子視圖血久。即它已經(jīng)被deatach
(標記為FLAG_TMP_DETACHED
狀態(tài))了氧吐。這種View
是可以被立即復(fù)用的。它在復(fù)用時筑舅,如果數(shù)據(jù)沒有更新陨舱,是不需要調(diào)用onBindViewHolder
方法的。如果數(shù)據(jù)更新了误墓,那么需要重新調(diào)用onBindViewHolder
谜慌。
mAttachedScrap
和mChangedScrap
中的View復(fù)用主要作用在adapter.notifyXXX
時。這時候就會產(chǎn)生很多scrap
狀態(tài)的view
欣范。 也可以把它理解為一個ViewHolder
的緩存。不過在從這里獲取ViewHolder
時完全是根據(jù)ViewHolder
的position
而不是item type
杖刷。如果在notifyXX
時data已經(jīng)被移除掉你,那么其中對應(yīng)的ViewHolder
也會被移除掉役听。
mCacheViews
可以把它理解為RecyclerView
的一級緩存。它的默認大小是3, 從中可以根據(jù)item type
或者position
來獲取ViewHolder
甜滨×鲂洌可以通過RecyclerView.setItemViewCacheSize()
來改變它的大小。
RecycledViewPool
它是一個可以被復(fù)用的ViewHolder
緩存池艾扮。即可以給多個RecycledView
來設(shè)置統(tǒng)一個RecycledViewPool
占婉。這個對于多tab feed流
應(yīng)用可能會有很顯著的效果。它內(nèi)部利用一個ScrapData
來保存ViewHolder
集合:
class ScrapData {
final ArrayList<ViewHolder> mScrapHeap = new ArrayList<>();
int mMaxScrap = DEFAULT_MAX_SCRAP; //最多緩存5個
long mCreateRunningAverageNs = 0;
long mBindRunningAverageNs = 0;
}
SparseArray<ScrapData> mScrap = new SparseArray<>(); //RecycledViewPool 用來保存ViewHolder的容器
一個ScrapData
對應(yīng)一種type
的ViewHolder
集合酌予∨壮妫看一下它的獲取ViewHolder
和保存ViewHolder
的方法:
//存
public void putRecycledView(ViewHolder scrap) {
final int viewType = scrap.getItemViewType();
final ArrayList<ViewHolder> scrapHeap = getScrapDataForType(viewType).mScrapHeap;
if (mScrap.get(viewType).mMaxScrap <= scrapHeap.size()) return; //到最大極限就不能放了
scrap.resetInternal(); //放到里面简僧,這個view就相當于和原來的信息完全隔離了,只記得他的type广凸,清除其相關(guān)狀態(tài)
scrapHeap.add(scrap);
}
//取
private ScrapData getScrapDataForType(int viewType) {
ScrapData scrapData = mScrap.get(viewType);
if (scrapData == null) {
scrapData = new ScrapData();
mScrap.put(viewType, scrapData);
}
return scrapData;
}
以上所述谅海,是RecycledView
最核心的組成部分(本文并沒有描述動畫的部分)蹦浦。
下一篇文章會分析RecyclerView的刷新機制
歡迎關(guān)注我的Android進階計劃〗耐啵看更多干貨