RecyclerView的超強(qiáng)輔助Graywater——理論篇

關(guān)于Graywater的系列文章

  1. RecyclerView的超強(qiáng)輔助Graywater——理論篇
  2. RecyclerView的超強(qiáng)輔助Graywater——基礎(chǔ)實(shí)操篇
  3. RecyclerView的超強(qiáng)輔助Graywater——點(diǎn)擊事件
  4. RecyclerView的超強(qiáng)輔助Graywater——綜合實(shí)操篇

Graywater是一個(gè)什么東西呢?它是由Tumblr開(kāi)源的一個(gè)代替RecyclerView.Adapter的類庫(kù)。Graywater將RecyclerView.Adapter拆解并重新設(shè)計(jì)封裝后,能使復(fù)雜多重結(jié)構(gòu)的RecyclerView在使用時(shí)如絲般順滑。

我將從四個(gè)問(wèn)題來(lái)帶大家了解什么是Graywater。

  • 問(wèn)題一:Graywater是什么

  • 問(wèn)題二:Graywater特點(diǎn)是什么?

  • 問(wèn)題三:Graywater原理是什么?

  • 問(wèn)題四:與原有的RecyclerView.Adapter相比,Graywater重寫了哪些核心方法?

一個(gè)問(wèn)題一個(gè)問(wèn)題的來(lái)看。

第一個(gè)問(wèn)題:Graywater是什么?

Graywater是一個(gè)由Tumblr開(kāi)發(fā)的第三方類庫(kù),是RecyclerView的一個(gè)適配器(Adapter)。因?yàn)镚raywater的多模塊設(shè)計(jì)方式,所以在繼承GraywaterAdapter時(shí),需要同時(shí)實(shí)現(xiàn)Graywater中各個(gè)模塊的相關(guān)類,來(lái)實(shí)現(xiàn)Graywater的特點(diǎn)。它最大的好處是能高效的處理復(fù)雜的列表,使復(fù)雜的列表使用起來(lái)如絲般順滑。

看一下官方Demo的GIF圖:

Graywater1.gif

下面是我寫的一個(gè)Demo的GIF圖,我寫的這個(gè)Demo不是很復(fù)雜,只是起到一個(gè)拋磚引玉的結(jié)果,Graywater還能實(shí)現(xiàn)更復(fù)雜的效果。

Graywater2.gif

第二個(gè)問(wèn)題:Graywater有什么特點(diǎn)?

通常我們?cè)谑褂肦ecyclerView.Adapter時(shí),是將數(shù)據(jù)集合(model)和對(duì)應(yīng)的ViewHolder相匹配,這種結(jié)構(gòu)用在存在大量樣式復(fù)雜的View時(shí)候,很容易變得卡頓。

下圖是一個(gè)普通的列表,為了提高體驗(yàn),超過(guò)屏幕部分的部分其實(shí)是可以回收的:

列表顯示.png

為了將超過(guò)屏幕的部分給回收,Tumblr采用了以下2個(gè)設(shè)計(jì)來(lái)提高性能并減少內(nèi)存。

  • Viewholders能夠被相同或者不同類型的models所共享,上圖中item#1和item#2的body viewholder就可以被共享。

  • 一個(gè)Model能擁有不同的Viewholders,一個(gè)item對(duì)應(yīng)一個(gè)Model,所以一個(gè)item也就能擁有無(wú)數(shù)個(gè)body viewholders。

這樣做的結(jié)果是能使用最少數(shù)量的ViewHolders來(lái)最大化內(nèi)存的使用率,同時(shí)還能減少內(nèi)存的使用。

第三個(gè)問(wèn)題:Graywater原理是什么?

在討論這個(gè)問(wèn)題前,我們先眼熟一下這張圖,這張圖概括了Graywater的設(shè)計(jì)。

原理圖1.png

這張圖里面涉及到了5個(gè)類:

  • Model
  • ViewHolder
  • ViewHolderCreator
  • Binder
  • ItemBinder

Model和ViewHolder是在使用RecyclerView時(shí)本來(lái)就會(huì)用到的,但是這2個(gè)類,因?yàn)镚raywater的設(shè)計(jì)原因,會(huì)跟在RecyclerView.Adapter使用時(shí)有一些區(qū)別。在下一篇基礎(chǔ)實(shí)操篇中可以看到。同時(shí)為了實(shí)現(xiàn)問(wèn)題2中的2個(gè)特點(diǎn),Tumblr在這兩者間添加了Binder類。來(lái)把model(T)數(shù)據(jù)綁定到 viewholder (VH)視圖上。

+-------+     +--------+     +------------+
| Model | --> | Binder | --> | ViewHolder |
+-------+     +--------+     +------------+

同時(shí),Graywater也不再追求單一的model與viewholder之間一對(duì)一的關(guān)系,因?yàn)閱我坏膍odel只能產(chǎn)生單一的視圖。而是將這兩者之間的關(guān)系變成了一對(duì)多的關(guān)系,這樣Adapter的靈活度就大大提升,一個(gè)Item就可以有Head、Body和Footer(其中的ViewHolder可以任意添加,沒(méi)有限制)。

                           +--------+     +------------+
                      /--> | Binder | --> | ViewHolder |
+-------+     +---+  /     +--------+     +------------+
| Model | --> | ? | *----> | Binder | --> | ViewHolder |
+-------+     +---+  \     +--------+     +------------+
                      \--> | Binder | --> | ViewHolder |
                           +--------+     +------------+

為了管理這種一對(duì)多關(guān)系,所以又添加Itembinder這一個(gè)類。所以就有了最開(kāi)始的原理圖:

原理圖1.png

ItemBinder用來(lái)管理一個(gè)Item中所有的Binder類,有一個(gè)getBinderList()方法來(lái)返回所管理的binder集合。同時(shí)在實(shí)現(xiàn)ItemBinder接口時(shí),需要傳入Model的類型,ItemBinder需要知道它所對(duì)應(yīng)的的數(shù)據(jù)類型(Model)是什么。

Binder<? super T, ? extends VH>接口中會(huì)傳入Model和ViewHolder的類型。Binder類就將model和ViewHolder聯(lián)系了起來(lái)。

所以一環(huán)扣一環(huán),各個(gè)類的關(guān)系也就建立起來(lái)了,下圖可以更直觀的展示:

原理.png

圖中還剩一個(gè)ViewHolderCreator是干嘛的呢?

在RecyclerView.Adapter的onCreateViewHolder()方法中需要?jiǎng)?chuàng)建ViewHolder,這個(gè)時(shí)候ViewHolderCreator就派上用場(chǎng)了。

ViewHolderCreator是一種獨(dú)立于model來(lái)創(chuàng)建viewholder的方式(在模型和視圖之間具有一對(duì)一關(guān)系的其他庫(kù)中,此代碼將存在于模型中 ,例如Epoxy)。

針對(duì)某一個(gè)類型的Item,創(chuàng)建對(duì)應(yīng)的5個(gè)基本類,并建立相應(yīng)的關(guān)系,關(guān)系全部建立好之后,就可以開(kāi)始分別實(shí)現(xiàn),得到一個(gè)高性能的Adapter。

第四個(gè)問(wèn)題:與原有的RecyclerView.Adapter相比,Graywater重寫了哪些核心方法?

Graywater將常用的4個(gè)方法全都重寫了

  • getItemCount()
  • getItemViewType(int position)
  • onCreateViewHolder(ViewGroup parent, int viewType)
  • onBindViewHolder(RecyclerView.ViewHolder holder, int position)

前兩個(gè)方法我就不講了,大家可以自己去看Graywater的源碼Github地址,主要說(shuō)說(shuō)后面兩個(gè)。

onCreateViewHolder(ViewGroup parent, int viewType)

在RecyclerView.Adapter中,在 onCreateViewHolder(ViewGroup parent, int viewType)里我們需要返回一個(gè)ViewHolder對(duì)象。而在Graywater中,這件事就由ViewHolerCreator代勞了。

GraywaterAdapter onCreateViewHolder(ViewGroup parent, int viewType)的源碼:

    @Override
    public VH onCreateViewHolder(final ViewGroup parent, final int viewType) {
        return (VH) mViewHolderCreatorMap.get(getViewHolderClass(viewType)).create(parent);
    }

從源碼里看到,有一個(gè)mViewHolderCreatorMap集合,這個(gè)集合中key值是ViewHolder的class類型Class<? extends VH>,value是ViewHolderCreator。從mViewHolderCreatorMap中獲取到ViewHolderCreator,再通過(guò)create()方法創(chuàng)建ViewHolder,當(dāng)然create()方法是由我們?cè)趯?shí)現(xiàn)ViewHolderCreator接口時(shí)來(lái)實(shí)現(xiàn)的。

也就是關(guān)系

+------------+     +-------------------+
| ViewHolder | <-- | ViewHolderCreator |
+------------+     +-------------------+

onBindViewHolder(RecyclerView.ViewHolder holder, int position)

這是RecyclerView.Adapter最核心的方法,Graywater通過(guò)Binder在這個(gè)方法中,將Model數(shù)據(jù)和ViewHolder視圖綁定起來(lái)。

    @Override
    @SuppressLint("RecyclerView")
    public void onBindViewHolder(final VH holder, final int viewHolderPosition) {

        final BinderResult result = computeItemAndBinderIndex(viewHolderPosition);
        final Binder binder = result.getBinder();

        if (binder != null && result.item != null) {

            if (mPreviousBoundViewHolderPosition == NO_PREVIOUS_BOUND_VIEWHOLDER) {
                prepare(viewHolderPosition, binder, result.item, result.binderList, result.binderIndex);
            }

            final ActionListener actionListener = mActionListenerMap.get(getModelType(result.item));

            binder.bind(result.item, holder, result.binderList, result.binderIndex, actionListener);

            prepareInternal(viewHolderPosition);
            mPreviousBoundViewHolderPosition = viewHolderPosition;
        }
    }

在Binder類中我們需要重寫一個(gè)bind()方法(有點(diǎn)類似onBindViewHolder,把數(shù)據(jù)給到view)。從這里我們就看到,bind()方法是怎么使用的了。

最核心的代碼:

 binder.bind(result.item, holder, result.binderList, result.binderIndex, actionListener);

BinderResult是GraywaterAdapter中的一個(gè)內(nèi)部類,擁有著與ViewHolder相關(guān)的Model、Binder的引用。

在binder和model不為空的情況下,將

  • model(result.item)
  • holder
  • binder集合(result.binderList)
  • 當(dāng)前viewholder的位置(result.binderIndex)
  • actionListener

作為參數(shù)傳遞到我們重寫的bind()方法中,在bind方法中將數(shù)據(jù)model映射到view視圖上,RecyclerView就能顯示出數(shù)據(jù)了。

理論部分差不多就講完了,下一篇就是實(shí)戰(zhàn)了。

P.S.
Graywater Github地址

如果對(duì)你有幫助的話,點(diǎn)贊、評(píng)論、贊賞都是對(duì)我的鼓勵(lì),也是支持我寫下去的動(dòng)力,謝謝!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容