一、前言
RecyclerView (下文简称RV)是安卓中列表数据项展示的最佳载体,也是开发、面试中的常客。
RV 要学习的东西很多 Adapter,LayoutManager,ItemDecoration,ItemAnimator,ViewHolder。。。
需要解决的场景也很多, 如头/底部刷新,多布局,点击事件,以及RV 的封装等。。。
这里先来总结下RV中的职责分配及其分工。
一 、RV 中的角色分工
RecyclerView.LayoutManager 负责计算布局
RecyclerView.Adapter 负责将列表数据进行适配展示
RecyclerView.ViewHolder 负责渲染Item
RecyclerView.ItemDecoration 负责items间隔
RecyclerView.ItemAnimator 负责item 添加删除时的动画
RV 的功能很多, 但具体功能是由其内部的实现类完成的,其本身相当于外观模式中面向外部的统一接口,我们只需要设置好注入给RV 就好, 其内部各司其职,协调行动 共同实现了RV 的功能。
RV 分工明确,本文讲解的是RV 的增删改的相关内容, 也就是Adapter 部分。
二、RV 增删改 之api 使用
Adapter 有一系列notifyxxx 方法, 数据在增删改后列表的刷新工作就是通过调用这些方法完成。下图就是Adapter 的一系列比较多的notifyxxx, 当然不用死记硬背,去除 其中两个Object notify 和notifyAll方法, 可以分成两类, 其中一类是只有一个的notifyDataSetChanged 它用来全局刷新 更耗性能 ,优点是 一键刷新 简单直接。
另一类是支持局部刷新的 notifyItemxxx方法,在刷新的时候带动画,也更省性能, 缺点是 使用的时候需要注意参数的设置,设置错误会出现闪退和刷新时数据与视图不匹配的现象。
下面就分别看一下api 的使用,和实现效果。
2.1、增
1. notifyItemInserted(int pos); 参数为添加元素的小标,限一个元素。
//增
mMsgList.add(2, new Msg("第" + (i++) +"大道", R.mipmap.love1));
mWhiteAdapter.notifyItemInserted(2);
2、notifyItemRangeInserted(int positionStart, int itemCount);
第一个参数为添加若干元素的最小下标, 第二个参数为 添加的个数; 限连续元素的添加。
//批量增
mMsgList.add(2, new Msg("第" + (i++) +"大道", R.mipmap.love3));
mMsgList.add(3, new Msg("第" + (i++) +"大道", R.mipmap.love3));
mMsgList.add(4, new Msg("第" + (i++) +"大道", R.mipmap.love3));
mWhiteAdapter.notifyItemRangeInserted(2, 3);
根据测试结果 positionStart 有误时可能会导致刷新时数据和视图不匹配,itemcount 有误时 会导致闪退。
2.3、改
2.3.1、notifyItemChanged
新元素替换原有的元素后 调用notifyItemChanged, 参数为修改元素的下标, 元素可以不连续。
//改
mMsgList.remove(0);
mMsgList.add(0, new Msg("第" + (i++) +"大道", R.mipmap.love2));
mWhiteAdapter.notifyItemChanged(0);
2.3.2、修改原有的数据
交换元素的下标。
mWhiteAdapter.notifyItemMoved(4, 7);
api 的使用不难,平时使用注意一下方法的 限制和参数的设置就行了。
下面white 会对Adapter进行源码分析。
三、Adapter 源码讲解
Adapter 的局部刷新 内部实现原理相同,这里选择Adapter 的 notifyItemRangeInserted 进行讲解。
可以看到Adapter 内部调用了mObservable 的同名方法,从属性命名可以看出 此处使用了观察者设计模式的思想,mObservable 扮演的是被观察的目标类对象,可暂且理解为 目标类对象通过此方法通知目标类而达到列表刷新的目的,那么观察者是谁呢?接着往下找, mObservable 是AdapterDataObservable 实例 ,其继承自Observable<AdapterDataObserver>。
AdapterDataObservable 从尾至首依次 又调用了 mObservers 元素的的元素onItemRangeInserted 方法, 这是典型的观察者模式思想,mObservers 是观察者的集合, 观察者具体类型为RecyclerViewDataObserver 。
那么观察者是如何注入到目标类中的呢,其实这个过程早在RV setAdapter 方法中就完成了。
private void setAdapterInternal(Adapter adapter, boolean compatibleWithPrevious,
boolean removeAndRecycleViews) {
...
final Adapter oldAdapter = mAdapter;
mAdapter = adapter;
if (adapter != null) {
** adapter.registerAdapterDataObserver(mObserver);**//此处完成观察者的注入
adapter.onAttachedToRecyclerView(this);
}
...
}
下图是RecyclerViewDataObserver 的同名方法:
RecyclerViewDataObserver 方法内部中:
assertNotInLayoutOrScroll 方法防止在滑动过程中刷新;
mAdapterHelper是 处理适配器更新操作的助手类, 这里的方法需要更新列表返回true。
triggerUpdateProcessor 方法。
前两个方法好理解,主要看第三个方法的操作,white 查了一下,此方法只有在调用 局部刷新方法的时候才会执行到, 全局刷新不会进入到这个方法的。
下图是全部刷新的执行,其每次执行的requestLayout 导致会RV 频繁计算布局,
局部刷新的方法:
此处会根据三个boolean 参数 的值执行不同的分支, 其中第一第二个参数,通常都满足,第二个参数为外部可通过RV设定,默认情况下为false;
这两个操作的主要区别在于是否执行RV 的 requestLayout 方法 而这个操作是更损耗性能的, 因此当我们使用 adapter 的局部方法刷新数据 且RV 高度不变时完全可以将mHasFixedSize 设置位true 已达到减轻性能的目的。
关于 RV. Adapter 的api 使用和源码分析就到这了,接下来white 会继续分析RV 的其余成员。