RecyclerView源码分析(一)--整体设计

阅读本文你大概需要10分钟

扯淡的导语

好像自从RecyclerView这个控件一出现,对这个控件使用方法的文章就层出不穷。我是一只都在使用这个控件,但是从来没有过深入的研究它。对于这样一个人人吹捧的控件,怎能不去研究一下。本想找找现成的分析源码的文章,结果并没有如愿。以前都是指望老罗,现在要亲自动手了。我是一个坚持写干货的人,这一段你就当没看到。

RecyclerView这个控件出来已经有一段时间了,如果看这篇文章的你,还没有使用过这个控件。那请先去学习怎样使用。不然看也白看。这里奉上一些关于介绍RecyclerView使用方法的优秀博客:

这几篇文章都是介绍RecyclerView使用方法的精品文章,其次还有很多大神做的RecyclerView的第三方库,这里我就不一一列举了,自己去按需搜索吧。

然后对于已经对RecyclerView有初步了解的读者,我们一起步入本文的正题。

RecyclerView的设计目的

研究一个现有的控件,先看看这个控件的设计目的是什么。其实就是看看RecyclerView是干什么的。官方对RecyclerView的介绍是很简短的一句话:

A flexible view for providing a limited window into a large data set.

一个用来为大量数据集合提供有限窗口的灵活的视图。我的翻译有点怪,自己看英文理解。介绍言简意赅,一针见血...好,我没词儿了。

其中关键的有两点:

  • providing a limited window into a large data set
  • flexible

针对与这两点我们可以看看RecyclerView的整体设计。

RecyclerView整体设计

RecyclerView中,针对要达到的功能点,都有相关的设计,下面分点来分析RecyclerView的设计。

RecyclerView数据展示的设计思路

在上一节中提到的,其设计目的的第一点就是展示大量数据。其实RecyclerView在这一点上和ListView等控件具有相同的设计思路,都是使用了设计模式中的适配器模式。不过即使你不知道适配器模式也不用担心。

首先呢,来看一张 巨丑无比 但是 简单明了 的结构图:

图一
图一

首先RecyclerView是一个ViewGroup,它和我们常用的各种Layout一样,是用来装很多子View的容器,那么它里面装的那些View是怎么来的呢?其实是来自ViewHolder中的itemView的。那么ViewHolder是从哪里生成的呢?显示的数据又是在哪里设置的呢?这就是Adapter的作用,它根据要展示数据的内容和类型,生成相应的ViewHolder,并对相应的View进行设置,从而展示出来。

如果从数据源出发就是,Adapter将要展示的数据根据其内容和类型,转化成对应的ViewHolder并对其进行设置,然后RecyclerView把ViewHolder中的itemView展示出来。

如果把这个图套用到适配器模式中,RecyclerView就是其中的Client,ViewHolder就是Target,Adapter自然就是Adapter,Data就是Adaptee。我这个图没有严格去按照适配器模式中的画是为了让即使不知道适配器模式的人也能看懂。

这个结构其实不通过源码也可以看出,在使用RecyclerView的时候也可以体会到。其中由Data生成对应用于展示的ViewHolder,就是通过实现Adapter中的onCreateViewHolder(ViewGroup parent, int viewType);onBindViewHolder(VH holder, int position);这两个方法。

设计目的中的第一点我们清楚了,那么我们来看第二点。

RecyclerView flexible的设计思路

在研究和探讨这个问题的之前我们需要具体化flexible。那么RecyclerView有哪些地方体现出了flexible?个人拙见有以下几点:

  1. 布局
  2. 动画
  3. 装饰

这些大家基本也都知道。那么我们分别看它在每个功能点上面的设计:

RecyclerView布局策略设计思路

细心的读者应该在上图中发现,在ViewHolder到RecyclerView的箭头上有三个点,其实就是暗示了这其中还有很多的猫腻!

还是先上一张 巨丑无比 但 简单明了 的图。

图二
图二

RecyclerView布局十分灵活,是因为RecyclerView将自己的布局策略全权交给了LayoutManager。仔细阅读源码还可以发现,就连View的添加,都是通过LayoutManager完成的。LayoutManager所做的事情就是拿到ViewHolder中的itemView,然后根据LayoutManager中定义的布局策略,对itemView进行布局,然后添加到RecyclerView中。

因此使用者可以根据自己的需要,自定义布局策略,而这里系统提供好了三种布局策略,线性布局,网格布局和瀑布流布局。一般情况下这三种已经满足了我们的需求。如果不能,用户可以自定义布局策略。

RecyclerView动画过程系统设计思路

RecyclerView作为一种展示大量数据的视图控件,难免会遇到数据变化的情况。例如添加,删除,更改等。当这些事情发生的时候,猿人往往喜欢通过动画来体现这种变化。那么在RecyclerView中便提供了一种非常灵活的动画机制。

同样先上一张 巨丑无比 但 简单明了 的图。

图三
图三

首先,达到数据改变触发动画,我们通常使用Adapter中的notifyXXX方法即可。但是其内部是如何工作的呢?

其实notify系列的方法可以看作是发出一个事件,在这里Adapter和RecyclerView的工作原理,是一个典型的观察者模式。

RecyclerView是观察者,Adapter是可观察的,在设置Adapter的时候RecyclerView订阅观察事件,当Adapter中的数据发生改变的时候通知RecyclerView。然后RecyclerView接到通知之后进行了很多处理。并触发重新布局。在布局过程中又经过一系列处理,将这些动画的信息存储到ViewInfoStore中。在布局结束的时候由ViewInfoStore统一处理并通过CallBack中的方法调用ItemAnimator中的方法执行动画。

RecyclerView动画的灵活性是通过ItemAnimator实现的。各位猿们可以通过继承ItemAnimator,然后实现里面的方法,来实现各种各样的动画效果。

RecyclerView装饰系统设计思路

这里其实并没有什么好讲的,实现ItemDecoration类中的抽象函数即可。RecyclerView内部就是在onDraw的时候执行ItemDecoration的onDraw,在draw的时候执行ItemDecoration的onDrawOver函数。在计算itemView的padding的时候将getItemOffsets得到的Rect加入其中,从而空出装饰内容的区域。其灵活性在于程序员们可以自定义ItemDecoration,实现各种各样的装饰。

对于ItemDecoration有一篇文章介绍的比较好,在这里推荐给大家。

建林大神的 深入理解 RecyclerView 系列之一:ItemDecoration其中也进行了深入讲解,而且我觉的可以了,这部分源码也没多少,很简单。

RecyclerView视图复用的设计思路

结合前两节的内容,我们的结构图应该成这个样子了(动画部分于该节无关,省略动画部分结构图):

图四
图四

其中ViewHolder的那一列很奇怪,是有多少个Data就有多少ViewHolder吗?ViewHolder是存储在哪里的?

那么将RecyclerView的复用结构补充上。又一张 巨丑无比 但 简单明了 的图。

图五
图五

这个相对于图四多了一个Recycler和RecyclerViewPool。这两个可能都不熟悉,那么对这两个类进行一个简单的介绍:

Recycler

A Recycler is responsible for managing scrapped or detached item views for reuse.

一个Recycler是负责管理成为碎片的视图或者已经detached的视图,从而实现View的复用。

RecyclerViewPool

RecycledViewPool lets you share Views between multiple RecyclerViews.

RecycledViewPool可以让你在多个RecyclerView之间分享视图

翻译的不好,不能忍的看原文。

介绍都说的很明白了,还有其实ViewHolder的创建和bind都是由Recycler执行的。还有LayoutManager获得ViewHolder的itemView,也是通过Recycler提供的。简单介绍一下Recycler和RecyclerViewPool的内部结构。

  1. Recycler里有几个ViewHolder的容器,用来存储不同状态的ViewHolder,以便之后复用。其中ViewCacheExtension类,是用户可以自定义复用机制的类。
  2. RecyclerViewPool,这个可以从外部对多个RecyclerView设置同一个RecyclerViewPool,从而实现多个RecyclerView中的ViewHolder的复用。

总结

本章简单的介绍了RecyclerView内部设计的大体框架结构。接下来会详细的介绍主要流程和机制。整理源码很费体力,如果这篇文章让你有所收获,请不要吝惜你的喜欢,你的喜欢是我写作的动力。

推荐阅读更多精彩内容