RecyclerView的基本設計結構

susion發表於2018-12-12

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

下面這張圖是我擷取的RecyclerView的Structure:

RecyclerView的基本設計結構

本文著重看: ViewHolderAdapterAdapterDataObservableRecyclerViewDataObserverLayoutManager、、RecyclerRecyclerPool。 從而理解RecycledView的大致實現原理。

先用一張圖大致描述他們之間的關係,這張圖是adapter.notifyXX()RecyclerView的執行邏輯涉及到的一些類:

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 viewaddRecyclerView中。
  • mPosition : 標記當前的ViewHolderAdapter中所處的位置。
  • mItemViewType : 這個ViewHolderType,在ViewHolder儲存到RecyclerPool時,主要靠這個型別來對ViewHolder做複用。
  • mFlags : 標記ViewHolder的狀態,比如 FLAG_BOUND(顯示在螢幕上)FLAG_INVALID(無效,想要使用必須rebound)FLAG_REMOVED(已被移除)等。

Adapter

它的工作是把dataView繫結,即上面說的一個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();

}
}複製程式碼

即:

  1. 它引用著一個資料來源集合dataSource
  2. getItemCount()用來告訴RecyclerView展示的總條目
  3. 它並不是直接對映data ->
    ViewHolder
    , 而是 data position ->
    data type ->
    viewholder
    。 所以對於ViewHolder來說,它知道的只是它的view type

AdapterDataObservable

Adapter是資料來源的直接接觸者,當資料來源發生變化時,它需要通知給RecyclerView。這裡使用的模式是觀察者模式AdapterDataObservable是資料來源變化時的被觀察者。RecyclerViewDataObserver是觀察者。在開發中我們通常使用adapter.notifyXX()來重新整理UI,實際上Adapter會呼叫AdapterDataObservablenotifyChanged():

    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的佈局管理者,RecyclerViewonLayout時,會利用它來layoutChildren,它決定了RecyclerView中的子View的擺放規則。但不止如此, 它做的工作還有:

  1. 測量子View
  2. 對子View進行佈局
  3. 對子View進行回收
  4. 子View動畫的排程
  5. 負責RecyclerView滾動的實現

Recycler

對於LayoutManager來說,它是ViewHolder的提供者。對於RecyclerView來說,它是ViewHolder的管理者,是RecyclerView最核心的實現。下面這張圖大致描述了它的組成:

RecyclerView的基本設計結構

scrap list

final ArrayList<
ViewHolder>
mAttachedScrap = new ArrayList<
>
();
ArrayList<
ViewHolder>
mChangedScrap = null;
複製程式碼
  • View Scrap狀態

相信你在許多RecyclerViewcrash log中都看到過這個單詞。它是指ViewRecyclerView佈局期間進入分離狀態的子檢視。即它已經被deatach(被標記為FLAG_TMP_DETACHED狀態)了。這種View是可以被立即複用的。它在複用時,如果資料沒有更新,是不需要呼叫onBindViewHolder方法的。如果資料更新了,那麼需要重新呼叫onBindViewHolder

mAttachedScrapmChangedScrap中的View複用主要作用在adapter.notifyXXX時。這時候就會產生很多scrap狀態的view。 也可以把它理解為一個ViewHolder的快取。不過在從這裡獲取ViewHolder時完全是根據ViewHolderposition而不是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對應一種typeViewHolder集合。看一下它的獲取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進階計劃。看更多幹貨

來源:https://juejin.im/post/5c10737de51d457926430f99

相關文章