RecyclerView
作為Android開發中最常用的View之一。很多App
的feed流都是使用RecyclerView
來實現的。加深對於RecyclerView
的掌握對於開發效率和開發質量都有很重要的意義。接下來我打算從原始碼 角度剖析RecyclerView
的實現,加深對於RecycledView
的瞭解。RecyclerView
的原始碼實現還是很龐大的。本文就先來看一下RecyclerView
的整體設計,瞭解其核心實現類的作用以及大致實現原理。
下面這張圖是我擷取的RecyclerView的Structure:
本文著重看: ViewHolder
、Adapter
、AdapterDataObservable
、RecyclerViewDataObserver
、LayoutManager
、、Recycler
、RecyclerPool
。 從而理解RecycledView
的大致實現原理。
先用一張圖大致描述他們之間的關係,這張圖是adapter.notifyXX()
時RecyclerView
的執行邏輯涉及到的一些類:
ViewHolder
對於Adapter
來說,一個ViewHolder
就對應一個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
做複用。 - mFlags : 標記
ViewHolder
的狀態,比如FLAG_BOUND(顯示在螢幕上)
、FLAG_INVALID(無效,想要使用必須rebound)
、FLAG_REMOVED(已被移除)
等。
Adapter
它的工作是把data
和View
繫結,即上面說的一個data
對應一個ViewHolder
。主要負責ViewHolder
的建立以及資料變化時通知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的實現
mObservable.notifyChanged();
}
}
複製程式碼
即:
- 它引用著一個資料來源集合
dataSource
getItemCount()
用來告訴RecyclerView
展示的總條目- 它並不是直接對映
data -> ViewHolder
, 而是data position -> data type -> viewholder
。 所以對於ViewHolder
來說,它知道的只是它的view type
AdapterDataObservable
Adapter
是資料來源的直接接觸者,當資料來源發生變化時,它需要通知給RecyclerView
。這裡使用的模式是觀察者模式
。AdapterDataObservable
是資料來源變化時的被觀察者。RecyclerViewDataObserver
是觀察者。
在開發中我們通常使用adapter.notifyXX()
來重新整理UI,實際上Adapter
會呼叫AdapterDataObservable
的notifyChanged()
:
public void notifyChanged() {
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onChanged();
}
}
複製程式碼
邏輯很簡單,即通知Observer
資料發生變化。
RecyclerViewDataObserver
它是RecycledView
用來監聽Adapter
資料變化的觀察者:
public void onChanged() {
mState.mStructureChanged = true; // RecycledView每一次UI的更新都會有一個State
processDataSetCompletelyChanged(true);
if (!mAdapterHelper.hasPendingUpdates()) {
requestLayout();
}
}
複製程式碼
LayoutManager
它是RecyclerView
的佈局管理者,RecyclerView
在onLayout
時,會利用它來layoutChildren
,它決定了RecyclerView
中的子View的擺放規則。但不止如此, 它做的工作還有:
- 測量子View
- 對子View進行佈局
- 對子View進行回收
- 子View動畫的排程
- 負責
RecyclerView
滾動的實現 - ...
Recycler
對於LayoutManager
來說,它是ViewHolder
的提供者。對於RecyclerView
來說,它是ViewHolder
的管理者,是RecyclerView
最核心的實現。下面這張圖大致描述了它的組成:
scrap list
final ArrayList<ViewHolder> mAttachedScrap = new ArrayList<>();
ArrayList<ViewHolder> mChangedScrap = null;
複製程式碼
View Scrap狀態
相信你在許多RecyclerView
的crash log
中都看到過這個單詞。它是指View
在RecyclerView
佈局期間進入分離狀態的子檢視。即它已經被deatach
(被標記為FLAG_TMP_DETACHED
狀態)了。這種View
是可以被立即複用的。它在複用時,如果資料沒有更新,是不需要呼叫onBindViewHolder
方法的。如果資料更新了,那麼需要重新呼叫onBindViewHolder
。
mAttachedScrap
和mChangedScrap
中的View複用主要作用在adapter.notifyXXX
時。這時候就會產生很多scrap
狀態的view
。 也可以把它理解為一個ViewHolder
的快取。不過在從這裡獲取ViewHolder
時完全是根據ViewHolder
的position
而不是item type
。如果在notifyXX
時data已經被移除掉你,那麼其中對應的ViewHolder
也會被移除掉。
mCacheViews
可以把它理解為RecyclerView
的一級快取。它的預設大小是3, 從中可以根據item type
或者position
來獲取ViewHolder
。可以通過RecyclerView.setItemViewCacheSize()
來改變它的大小。
RecycledViewPool
它是一個可以被複用的ViewHolder
快取池。即可以給多個RecycledView
來設定統一個RecycledViewPool
。這個對於多tab feed流
應用可能會有很顯著的效果。它內部利用一個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
對應一種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,清除其相關狀態
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重新整理機制
歡迎關注我的Android進階計劃。看更多幹貨