RecyclerView的基本設(shè)計結(jié)構(gòu)

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:

類的組成.png

本文著重看: ViewHolder溪食、Adapter错沃、AdapterDataObservableRecyclerViewDataObserver玉掸、LayoutManager登疗、、Recycler断傲、RecyclerPool认罩。 從而理解RecycledView的大致實現(xiàn)原理续捂。

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

RecyclerView組成類之間的關(guān)系.png

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 viewaddRecyclerView中酒繁。
  • mPosition : 標記當前的ViewHolderAdapter中所處的位置控妻。
  • mItemViewType : 這個ViewHolderType弓候,在ViewHolder保存到RecyclerPool時,主要靠這個類型來對ViewHolder做復(fù)用彰居。
  • mFlags : 標記ViewHolder的狀態(tài)撰筷,比如 FLAG_BOUND(顯示在屏幕上)毕籽、FLAG_INVALID(無效,想要使用必須rebound)溶握、FLAG_REMOVED(已被移除)等蒸播。

Adapter

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

即:

  1. 它引用著一個數(shù)據(jù)源集合dataSource
  2. getItemCount()用來告訴RecyclerView展示的總條目
  3. 它并不是直接映射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)用AdapterDataObservablenotifyChanged():

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

  1. 測量子View
  2. 對子View進行布局
  3. 對子View進行回收
  4. 子View動畫的調(diào)度
  5. 負責RecyclerView滾動的實現(xiàn)
  6. ...

Recycler

對于LayoutManager來說兑凿,它是ViewHolder的提供者。對于RecyclerView來說咐鹤,它是ViewHolder的管理者慷暂,是RecyclerView最核心的實現(xiàn)晨雳。下面這張圖大致描述了它的組成:

Recycler的組成.png

scrap list

final ArrayList<ViewHolder> mAttachedScrap = new ArrayList<>();
ArrayList<ViewHolder> mChangedScrap = null;
  • View Scrap狀態(tài)

相信你在許多RecyclerViewcrash log中都看到過這個單詞。它是指ViewRecyclerView布局期間進入分離狀態(tài)的子視圖血久。即它已經(jīng)被deatach(標記為FLAG_TMP_DETACHED狀態(tài))了氧吐。這種View是可以被立即復(fù)用的。它在復(fù)用時筑舅,如果數(shù)據(jù)沒有更新陨舱,是不需要調(diào)用onBindViewHolder方法的。如果數(shù)據(jù)更新了误墓,那么需要重新調(diào)用onBindViewHolder谜慌。

mAttachedScrapmChangedScrap中的View復(fù)用主要作用在adapter.notifyXXX時。這時候就會產(chǎn)生很多scrap狀態(tài)的view欣范。 也可以把它理解為一個ViewHolder的緩存。不過在從這里獲取ViewHolder時完全是根據(jù)ViewHolderposition而不是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)一種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广凸,清除其相關(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進階計劃〗耐啵看更多干貨

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市浦旱,隨后出現(xiàn)的幾起案子颁湖,更是在濱河造成了極大的恐慌,老刑警劉巖甥捺,帶你破解...
    沈念sama閱讀 212,884評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件镰禾,死亡現(xiàn)場離奇詭異唱逢,居然都是意外死亡,警方通過查閱死者的電腦和手機惶我,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評論 3 385
  • 文/潘曉璐 我一進店門绸贡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人捧挺,你說我怎么就攤上這事尿瞭。” “怎么了黑竞?”我有些...
    開封第一講書人閱讀 158,369評論 0 348
  • 文/不壞的土叔 我叫張陵很魂,是天一觀的道長檐涝。 經(jīng)常有香客問我法挨,道長凡纳,這世上最難降的妖魔是什么帝蒿? 我笑而不...
    開封第一講書人閱讀 56,799評論 1 285
  • 正文 為了忘掉前任葛超,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘页畦。我一直安慰自己,他們只是感情好独令,可當我...
    茶點故事閱讀 65,910評論 6 386
  • 文/花漫 我一把揭開白布燃箭。 她就那樣靜靜地躺著招狸,像睡著了一般邻薯。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上厕诡,一...
    開封第一講書人閱讀 50,096評論 1 291
  • 那天灵嫌,我揣著相機與錄音,去河邊找鬼猖凛。 笑死形病,一個胖子當著我的面吹牛客年,可吹牛的內(nèi)容都是我干的漠吻。 我是一名探鬼主播,決...
    沈念sama閱讀 39,159評論 3 411
  • 文/蒼蘭香墨 我猛地睜開眼绍傲,長吁一口氣:“原來是場噩夢啊……” “哼耍共!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起杠纵,我...
    開封第一講書人閱讀 37,917評論 0 268
  • 序言:老撾萬榮一對情侶失蹤比藻,失蹤者是張志新(化名)和其女友劉穎倘屹,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體务蝠,經(jīng)...
    沈念sama閱讀 44,360評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡馏段,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,673評論 2 327
  • 正文 我和宋清朗相戀三年毅弧,在試婚紗的時候發(fā)現(xiàn)自己被綠了当窗。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片崖面。...
    茶點故事閱讀 38,814評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡巫员,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出简识,到底是詐尸還是另有隱情,我是刑警寧澤奢赂,帶...
    沈念sama閱讀 34,509評論 4 334
  • 正文 年R本政府宣布,位于F島的核電站膳灶,受9級特大地震影響咱士,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜轧钓,卻給世界環(huán)境...
    茶點故事閱讀 40,156評論 3 317
  • 文/蒙蒙 一序厉、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧毕箍,春花似錦弛房、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至牺堰,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間颅围,已是汗流浹背伟葫。 一陣腳步聲響...
    開封第一講書人閱讀 32,123評論 1 267
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留渐溶,地道東北人茎辐。 一個月前我還...
    沈念sama閱讀 46,641評論 2 362
  • 正文 我出身青樓,卻偏偏與公主長得像依啰,于是被迫代替她去往敵國和親叹誉。 傳聞我的和親對象是個殘疾皇子长豁,可洞房花燭夜當晚...
    茶點故事閱讀 43,728評論 2 351

推薦閱讀更多精彩內(nèi)容

  • 目錄介紹 1.RecycleView的結(jié)構(gòu) 2.Adapter2.1 RecyclerView.Adapter扮演...
    楊充211閱讀 3,072評論 3 17
  • 1宅此、概述 Android文檔中是這么定義RecyclerView的:*A RecyclerView is a fl...
    高丕基閱讀 1,423評論 2 36
  • 這篇文章分三個部分青瀑,簡單跟大家講一下 RecyclerView 的常用方法與奇葩用法斥难;工作原理與ListView比...
    LucasAdam閱讀 4,381評論 0 27
  • 簡介: 提供一個讓有限的窗口變成一個大數(shù)據(jù)集的靈活視圖群扶。 術(shù)語表: Adapter:RecyclerView的子類...
    酷泡泡閱讀 5,154評論 0 16
  • 我和我的閨蜜都是在單位工作認識的盏道,兩個年輕的女孩初來乍到猜嘱,有些相同的家庭背景朗伶,相同的愛好益楼,都是表面文靜其實逗...
    梓薇薇閱讀 344評論 1 2