RecyclerView源码解析
总体框架
内部调用流程
设计思路
RecyclerView官网给出的定义是: A flexible view for providing a limited window into a large data set. 也就是在限定的视图内展示大量的数据
如上所示,RecyclerView表示只会和ViewHolder进行接触,Adapter的工作是将Data转换为RecyclerView认识的ViewHolder,因此RecyclerView就间接地认识Datas。
而RecyclerView仍然不想管理子view,于是交给了LayoutManager来帮助完成布局(桥接模式)。
但是LayoutManager这个大管家也有弱点,它只知道怎么将这一个个View布局在RecyclerView上,但是并不知道如何管理这些View,此时要有个回收期Recycler回收器做管理。当LayoutManager在需要View的时候向Recycler进行索取,当LayoutManager不需要View(试图滑出)的时候,就直接将废弃的View丢给Recycler。
到了这里,看似一切都很完美,但是RecyclerView还想让子View变动的动画要优雅,所以通过观察者模式,引入了ItemAnimator(观察者模式)。
设计模式
- 通过桥接模式,使RecyclerView将布局方式独立成LayoutManager,实现对布局的定制化。
- 通过组合模式,使RecyclerView通过dispatchLayout对item View进行布局绘制。
- 通过适配器模式,ViewHolder将RecyclerView与ItemView联系起来,使得RecyclerView方便操作ItemView。
- 通过观察者模式,给ViewHolder注册观察者,当调用notifyDataSetChanged时,就能重新绘制。
源码分析
设计结构
类关系图
- RecyclerViewDataObserver: 数据观察器
- Recycler: View 循环复用系统,核心部件
- SavedState: RecyclerView状态
- AdapterHelper:适配器更新
- ChildHelper:管理子View
- ViewInfoStore:存储子VIEW的动画信息
- Adapter:数据适配器
- LayoutManager:负责子VIEW的布局,核心部件
- ItemAnimator:Item动画
- ViewFlinger:快速滑动管理
- NestedScrollingChildHelper:管理子VIEW嵌套滑动
绘制详情
以我们平时使用RecyclerView的行为举例:
1 | recyclerView = (RecyclerView) findViewById(R.id.recyclerView); |
初始化整体流程图如下
RecyclerView layout(setLayoutManager)流程是什么样的?
layout过程中onLayoutChildren是什么流程?
- 找到anchor点
- 根据anchor一直向前布局,直到充满anchor点前面的所有区域
- 根据anchor一直向后布局,直到填充满anchor点后面所有区域,以垂直布局来说明,mAnchorInfo为布局锚点信息,包含了子控件在Y轴上起始绘制偏移量(coordinate),ItemView在Adapter中的索引信息(position)和布局方向(mLayoutFromEnd) ——指start、end方向。确定布局锚点,以此为起点向开始和结束方向填充ItemView。
setAdapter方法最终调用的流程?
- 如果之前存在Adapter,先移除原来的,注销观察者,从RecyclerView Detached。
- 根据参数,决定是否清除原来的ViewHolder。
- 重置AdapterHelper,更新Adapter,注册观察者。
RecyclerView的滑动过程是什么样的?
缓存逻辑是什么样的?
总共分为两级缓存
- mCachedViews,先进先出的数据结构,将新的View存入mCachedViews,移除头元素,并将头元素放入mRecyclerPool
- mRecyclerPool,可以多个ReyclerView共享,减少内存开销。
AdapterView与RecyclerView的区别?
RecyclerView中有哪些设计精巧的地方?
Bucket
用来表示不设上限的状态,Bucket是一个链表结构,当index大于64的时候,它便会去下一个Bucket寻找,Bucket不设上限的表示状态。
1 | static class Bucket { |
Pools
相当于Messages的池的概念,通过Message.obtain()可以在很多情况下避免创建新的对象
- RecycleView将Item的增删改封装为UpdateOp类
- ViewInfoStore类中的静态内部类infoRecord
使用RecyclerView有没有遇到不好用的地方?
RecyclerView也不是万能的,它的灵活性也是有一定限制的,比如我就遇到了一不是很好解决的问题:Recyler的缓存级别是一个Item的整个View,而我们没办法自定义缓存级别,这样说比较抽象,举个例子,我的某些Item的某个子View加载很耗时,所以我希望我在上下滑动的时候,Item的其它View是可以被回收利用的,但这个加载很耗时的View是不要重复使用的。即我希望用空间换取时间来获取滑动的流畅性。当然,这样的需求不常见,RecyclerView也不能很好的满足这一点。