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地址

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

推荐阅读更多精彩内容