RecyclerView的基本設計結構

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

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

本文着重看: ViewHolderAdapterAdapterDataObservableRecyclerViewDataObserverLayoutManager、、RecyclerRecyclerPool。 從而理解RecycledView的大體實現原理。緩存

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

ViewHolder

對於Adapter來講,一個ViewHolder就對應一個data。它也是Recycler緩存池的基本單元。ide

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:post

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():ui

public void notifyChanged() {
        for (int i = mObservers.size() - 1; i >= 0; i--) {
            mObservers.get(i).onChanged();
        }
    }
複製代碼

邏輯很簡單,即通知Observer數據發生變化。spa

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滾動的實現
  6. ...

Recycler

對於LayoutManager來講,它是ViewHolder的提供者。對於RecyclerView來講,它是ViewHolder的管理者,是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進階計劃。看更多幹貨

相關文章
相關標籤/搜索