RecyclerView的超强辅助Graywater——理论篇

关于Graywater的系列文章

  1. RecyclerView的超强辅助Graywater——理论篇
  2. RecyclerView的超强辅助Graywater——基础实操篇
  3. RecyclerView的超强辅助Graywater——点击事件
  4. RecyclerView的超强辅助Graywater——综合实操篇

Graywater是一个什么东西呢?它是由Tumblr开源的一个代替RecyclerView.Adapter的类库。Graywater将RecyclerView.Adapter拆解并重新设计封装后,能使复杂多重结构的RecyclerView在使用时如丝般顺滑。

我将从四个问题来带大家了解什么是Graywater。

  • 问题一:Graywater是什么

  • 问题二:Graywater特点是什么?

  • 问题三:Graywater原理是什么?

  • 问题四:与原有的RecyclerView.Adapter相比,Graywater重写了哪些核心方法?

一个问题一个问题的来看。

第一个问题:Graywater是什么?

Graywater是一个由Tumblr开发的第三方类库,是RecyclerView的一个适配器(Adapter)。因为Graywater的多模块设计方式,所以在继承GraywaterAdapter时,需要同时实现Graywater中各个模块的相关类,来实现Graywater的特点。它最大的好处是能高效的处理复杂的列表,使复杂的列表使用起来如丝般顺滑。

看一下官方Demo的GIF图:

Graywater1.gif

下面是我写的一个Demo的GIF图,我写的这个Demo不是很复杂,只是起到一个抛砖引玉的结果,Graywater还能实现更复杂的效果。

Graywater2.gif

第二个问题:Graywater有什么特点?

通常我们在使用RecyclerView.Adapter时,是将数据集合(model)和对应的ViewHolder相匹配,这种结构用在存在大量样式复杂的View时候,很容易变得卡顿。

下图是一个普通的列表,为了提高体验,超过屏幕部分的部分其实是可以回收的:

列表显示.png

为了将超过屏幕的部分给回收,Tumblr采用了以下2个设计来提高性能并减少内存。

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

  • 一个Model能拥有不同的Viewholders,一个item对应一个Model,所以一个item也就能拥有无数个body viewholders。

这样做的结果是能使用最少数量的ViewHolders来最大化内存的使用率,同时还能减少内存的使用。

第三个问题:Graywater原理是什么?

在讨论这个问题前,我们先眼熟一下这张图,这张图概括了Graywater的设计。

原理图1.png

这张图里面涉及到了5个类:

  • Model
  • ViewHolder
  • ViewHolderCreator
  • Binder
  • ItemBinder

Model和ViewHolder是在使用RecyclerView时本来就会用到的,但是这2个类,因为Graywater的设计原因,会跟在RecyclerView.Adapter使用时有一些区别。在下一篇基础实操篇中可以看到。同时为了实现问题2中的2个特点,Tumblr在这两者间添加了Binder类。来把model(T)数据绑定到 viewholder (VH)视图上。

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

同时,Graywater也不再追求单一的model与viewholder之间一对一的关系,因为单一的model只能产生单一的视图。而是将这两者之间的关系变成了一对多的关系,这样Adapter的灵活度就大大提升,一个Item就可以有Head、Body和Footer(其中的ViewHolder可以任意添加,没有限制)。

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

为了管理这种一对多关系,所以又添加Itembinder这一个类。所以就有了最开始的原理图:

原理图1.png

ItemBinder用来管理一个Item中所有的Binder类,有一个getBinderList()方法来返回所管理的binder集合。同时在实现ItemBinder接口时,需要传入Model的类型,ItemBinder需要知道它所对应的的数据类型(Model)是什么。

Binder<? super T, ? extends VH>接口中会传入Model和ViewHolder的类型。Binder类就将model和ViewHolder联系了起来。

所以一环扣一环,各个类的关系也就建立起来了,下图可以更直观的展示:

原理.png

图中还剩一个ViewHolderCreator是干嘛的呢?

在RecyclerView.Adapter的onCreateViewHolder()方法中需要创建ViewHolder,这个时候ViewHolderCreator就派上用场了。

ViewHolderCreator是一种独立于model来创建viewholder的方式(在模型和视图之间具有一对一关系的其他库中,此代码将存在于模型中 ,例如Epoxy)。

针对某一个类型的Item,创建对应的5个基本类,并建立相应的关系,关系全部建立好之后,就可以开始分别实现,得到一个高性能的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()方法创建ViewHolder,当然create()方法是由我们在实现ViewHolderCreator接口时来实现的。

也就是关系

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

onBindViewHolder(RecyclerView.ViewHolder holder, int position)

这是RecyclerView.Adapter最核心的方法,Graywater通过Binder在这个方法中,将Model数据和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,把数据给到view)。从这里我们就看到,bind()方法是怎么使用的了。

最核心的代码:

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

BinderResult是GraywaterAdapter中的一个内部类,拥有着与ViewHolder相关的Model、Binder的引用。

在binder和model不为空的情况下,将

  • model(result.item)
  • holder
  • binder集合(result.binderList)
  • 当前viewholder的位置(result.binderIndex)
  • actionListener

作为参数传递到我们重写的bind()方法中,在bind方法中将数据model映射到view视图上,RecyclerView就能显示出数据了。

理论部分差不多就讲完了,下一篇就是实战了。

P.S.
Graywater Github地址

如果对你有帮助的话,点赞、评论、赞赏都是对我的鼓励,也是支持我写下去的动力,谢谢!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容