RecyclerView的基本設計結構
RecyclerView
作為Android開發中最常用的View之一。很多 App
的feed流都是使用 RecyclerView
來實現的。加深對於 RecyclerView
的掌握對於開發效率和開發質量都有很重要的意義。接下來我打算從原始碼
角度剖析 RecyclerView
的實現,加深對於 RecycledView
的瞭解。 RecyclerView
的原始碼實現還是很龐大的。本文就先來看一下 RecyclerView
的整體設計,瞭解其核心實現類的作用以及大致實現原理。
下面這張圖是我擷取的 RecyclerView的Structure:

類的組成.png
本文著重看: ViewHolder
、 Adapter
、 AdapterDataObservable
、 RecyclerViewDataObserver
、 LayoutManager
、、 Recycler
、 RecyclerPool
。 從而理解 RecycledView
的大致實現原理。
先用一張圖大致描述他們之間的關係,這張圖是 adapter.notifyXX()
時 RecyclerView
的執行邏輯涉及到的一些類:

RecyclerView組成類之間的關係.png
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的擺放規則。但不止如此, 它做的工作還有:
RecyclerView
Recycler
對於 LayoutManager
來說,它是 ViewHolder
的提供者。對於 RecyclerView
來說,它是 ViewHolder
的管理者,是 RecyclerView
最核心的實現。下面這張圖大致描述了它的組成:

Recycler的組成.png
scrap list
final ArrayList<ViewHolder> mAttachedScrap = new ArrayList<>(); ArrayList<ViewHolder> mChangedScrap = null;
-
View Scrap狀態
相信你在許多 RecyclerView
的 crash log
中都看到過這個單詞。它是指 View
在 RecyclerView
佈局期間進入分離狀態的子檢視。即它已經被 deatach
(並不是呼叫了onDetatchToWindow方法, 是被標記為 FLAG_TMP_DETACHED
狀態)了。這種 View
是可以被立即複用的。它在複用時,如果資料沒有更新,是不需要呼叫 onBindViewHolder
方法的。如果資料更新了,那麼需要重新呼叫 onBindViewHolder
。
mAttachedScrap
和 mChangedScrap
中的View複用主要作用在 adapter.notifyXXX
時。這時候就會產生很多 scrap
狀態的 view
。 也可以把它理解為一個 ViewHolder
的快取。不過在從這裡獲取 ViewHolder
時完全是根據 ViewHolder
的 position
而不是 item type
。如果在 notifyXX
時data已經被移除掉你,那麼其中對應的 ViewHolder
也會被移除掉。
mCacheViews
可以把它理解為 RecyclerView
的一級快取。它的預設大小是2。只能減少不能增加。從中可以根據 item type
來獲取 ViewHolder
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
最核心的組成部分(本文並沒有描述動畫的部分)。接下來會繼續分析 RecycledView
是如何利用它們來工作起來的。
歡迎關注我的 ofollow,noindex">Android進階計劃 。看更多幹貨