關(guān)于Graywater的系列文章
- RecyclerView的超強輔助Graywater——理論篇
- RecyclerView的超強輔助Graywater——基礎(chǔ)實操篇
- RecyclerView的超強輔助Graywater——點擊事件
- RecyclerView的超強輔助Graywater——綜合實操篇
Graywater是一個什么東西呢?它是由Tumblr開源的一個代替RecyclerView.Adapter的類庫。Graywater將RecyclerView.Adapter拆解并重新設(shè)計封裝后,能使復(fù)雜多重結(jié)構(gòu)的RecyclerView在使用時如絲般順滑。
我將從四個問題來帶大家了解什么是Graywater。
問題一:Graywater是什么
問題二:Graywater特點是什么?
問題三:Graywater原理是什么?
問題四:與原有的RecyclerView.Adapter相比,Graywater重寫了哪些核心方法?
一個問題一個問題的來看。
第一個問題:Graywater是什么?
Graywater是一個由Tumblr開發(fā)的第三方類庫,是RecyclerView的一個適配器(Adapter)。因為Graywater的多模塊設(shè)計方式,所以在繼承GraywaterAdapter時,需要同時實現(xiàn)Graywater中各個模塊的相關(guān)類,來實現(xiàn)Graywater的特點。它最大的好處是能高效的處理復(fù)雜的列表,使復(fù)雜的列表使用起來如絲般順滑。
看一下官方Demo的GIF圖:

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

第二個問題:Graywater有什么特點?
通常我們在使用RecyclerView.Adapter時,是將數(shù)據(jù)集合(model)和對應(yīng)的ViewHolder相匹配,這種結(jié)構(gòu)用在存在大量樣式復(fù)雜的View時候,很容易變得卡頓。
下圖是一個普通的列表,為了提高體驗,超過屏幕部分的部分其實是可以回收的:

為了將超過屏幕的部分給回收,Tumblr采用了以下2個設(shè)計來提高性能并減少內(nèi)存。
Viewholders能夠被相同或者不同類型的models所共享,上圖中item#1和item#2的body viewholder就可以被共享。
一個Model能擁有不同的Viewholders,一個item對應(yīng)一個Model,所以一個item也就能擁有無數(shù)個body viewholders。
這樣做的結(jié)果是能使用最少數(shù)量的ViewHolders來最大化內(nèi)存的使用率,同時還能減少內(nèi)存的使用。
第三個問題:Graywater原理是什么?
在討論這個問題前,我們先眼熟一下這張圖,這張圖概括了Graywater的設(shè)計。

這張圖里面涉及到了5個類:
- Model
- ViewHolder
- ViewHolderCreator
- Binder
- ItemBinder
Model和ViewHolder是在使用RecyclerView時本來就會用到的,但是這2個類,因為Graywater的設(shè)計原因,會跟在RecyclerView.Adapter使用時有一些區(qū)別。在下一篇基礎(chǔ)實操篇中可以看到。同時為了實現(xiàn)問題2中的2個特點,Tumblr在這兩者間添加了Binder類。來把model(T)數(shù)據(jù)綁定到 viewholder (VH)視圖上。
+-------+ +--------+ +------------+
| Model | --> | Binder | --> | ViewHolder |
+-------+ +--------+ +------------+
同時,Graywater也不再追求單一的model與viewholder之間一對一的關(guān)系,因為單一的model只能產(chǎn)生單一的視圖。而是將這兩者之間的關(guān)系變成了一對多的關(guān)系,這樣Adapter的靈活度就大大提升,一個Item就可以有Head、Body和Footer(其中的ViewHolder可以任意添加,沒有限制)。
+--------+ +------------+
/--> | Binder | --> | ViewHolder |
+-------+ +---+ / +--------+ +------------+
| Model | --> | ? | *----> | Binder | --> | ViewHolder |
+-------+ +---+ \ +--------+ +------------+
\--> | Binder | --> | ViewHolder |
+--------+ +------------+
為了管理這種一對多關(guān)系,所以又添加Itembinder這一個類。所以就有了最開始的原理圖:

ItemBinder用來管理一個Item中所有的Binder類,有一個getBinderList()方法來返回所管理的binder集合。同時在實現(xiàn)ItemBinder接口時,需要傳入Model的類型,ItemBinder需要知道它所對應(yīng)的的數(shù)據(jù)類型(Model)是什么。
Binder<? super T, ? extends VH>接口中會傳入Model和ViewHolder的類型。Binder類就將model和ViewHolder聯(lián)系了起來。
所以一環(huán)扣一環(huán),各個類的關(guān)系也就建立起來了,下圖可以更直觀的展示:

圖中還剩一個ViewHolderCreator是干嘛的呢?
在RecyclerView.Adapter的onCreateViewHolder()方法中需要創(chuàng)建ViewHolder,這個時候ViewHolderCreator就派上用場了。
ViewHolderCreator是一種獨立于model來創(chuàng)建viewholder的方式(在模型和視圖之間具有一對一關(guān)系的其他庫中,此代碼將存在于模型中 ,例如Epoxy)。
針對某一個類型的Item,創(chuàng)建對應(yīng)的5個基本類,并建立相應(yīng)的關(guān)系,關(guān)系全部建立好之后,就可以開始分別實現(xiàn),得到一個高性能的Adapter。
第四個問題:與原有的RecyclerView.Adapter相比,Graywater重寫了哪些核心方法?
Graywater將常用的4個方法全都重寫了
- getItemCount()
- getItemViewType(int position)
- onCreateViewHolder(ViewGroup parent, int viewType)
- onBindViewHolder(RecyclerView.ViewHolder holder, int position)
前兩個方法我就不講了,大家可以自己去看Graywater的源碼Github地址,主要說說后面兩個。
onCreateViewHolder(ViewGroup parent, int viewType)
在RecyclerView.Adapter中,在 onCreateViewHolder(ViewGroup parent, int viewType)里我們需要返回一個ViewHolder對象。而在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);
}
從源碼里看到,有一個mViewHolderCreatorMap集合,這個集合中key值是ViewHolder的class類型Class<? extends VH>,value是ViewHolderCreator。從mViewHolderCreatorMap中獲取到ViewHolderCreator,再通過create()方法創(chuàng)建ViewHolder,當然create()方法是由我們在實現(xiàn)ViewHolderCreator接口時來實現(xiàn)的。
也就是關(guān)系
+------------+ +-------------------+
| ViewHolder | <-- | ViewHolderCreator |
+------------+ +-------------------+
onBindViewHolder(RecyclerView.ViewHolder holder, int position)
這是RecyclerView.Adapter最核心的方法,Graywater通過Binder在這個方法中,將Model數(shù)據(jù)和ViewHolder視圖綁定起來。
@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類中我們需要重寫一個bind()方法(有點類似onBindViewHolder,把數(shù)據(jù)給到view)。從這里我們就看到,bind()方法是怎么使用的了。
最核心的代碼:
binder.bind(result.item, holder, result.binderList, result.binderIndex, actionListener);
BinderResult是GraywaterAdapter中的一個內(nèi)部類,擁有著與ViewHolder相關(guān)的Model、Binder的引用。
在binder和model不為空的情況下,將
- model(result.item)
- holder
- binder集合(result.binderList)
- 當前viewholder的位置(result.binderIndex)
- actionListener
作為參數(shù)傳遞到我們重寫的bind()方法中,在bind方法中將數(shù)據(jù)model映射到view視圖上,RecyclerView就能顯示出數(shù)據(jù)了。
理論部分差不多就講完了,下一篇就是實戰(zhàn)了。
P.S.
Graywater Github地址
如果對你有幫助的話,點贊、評論、贊賞都是對我的鼓勵,也是支持我寫下去的動力,謝謝!