背景:RecyclerView 使用notifyDataSetChanged 会导致图片闪烁
具体原因可参看:RecyclerView 体验优化及入坑总结 的入坑篇第二个问题
一、RecyclerView 局部刷新不好用
RecyclerView 除了配置动画、布局等方便外,相比ListView ,提供了不少数据刷新方式,除了常见的notifyDataSetChanged() 全局刷新外,还提供了很多局部刷新方式,列举如下:
上述局部刷新方式,看似好用(插入、更新、移动、删除),但实际不太好用,甚至基本无用。不好用的原因在于,不知何时去选择刷新方式 ,因而还是一股脑的使用notifyDataSetChanged() ,除此之外 使用较多的场景 :页面加载更多 ,使用notifyItemRangeChanged(int ,int),替他刷新方式根本没有使用过。
可能正因如此,Google 在 Support-v7:24:2.0 提供了DiffUtils 类,让我们正真意义上使用RecyclerView 的局部刷新。
二 、DiffUtil 介绍 及使用
DiffUtil是 Support-v7:24:2.0 中,中更新的工具类,主要是为了配合RecyclerView使用,通过比对新、旧两个数据集的差异,生成旧数据到新数据的最小变动,然后对有变动的数据项,进行局部刷新。
DiffUtil 核心内容 :
(1)DiffUtil.Callback
DiffUtil.Callback :具体用于限定数据集比对规则 ,内部主要有如下5个比较方法:
1)getOldListSize():旧数据集的长度;
2)getNewListSize():新数据集的长度
3)areItemsTheSame():判断是否是同一个item;
4)areContentsTheSame():如果item相同,此方法用于判断是否同一个 Item 的内容也相同;
5)getChangePayload() :如果item相同,内容不同,用 payLoad 记录这个 ViewHolder 中,具体需要更新那个View
从图2知 ,getChangePayload() 默认返回 null ,即整个item 全部刷新。
一般在getChangePayload()方法中调用super.getChangePayload() 即可,不做精细化刷新。
如果一个item 非常复杂,存在里面某个View 数据刷新,可以利用payLoad参数来实现,对应修改点 在 onBindViewHolder(HelloViewHolder holder, int position ) 的基础上实现带参的 onBindViewHolder ,同时在getChangedPayLoad()对应实现。
从上面分析可知:areItemsTheSame()、areContentsTheSame()、getChangePayload() 分别代表了不同量级的刷新。
(2)DiffUtil.DiffResult
DiffUtil.DiffResult : 比对数据集之后,返回的差异结果 ,通过DiffUtil.calculateDiff(diffCallback)(当数据量较大时,建议放在子线程中调用)得到。
DiffUtil.DiffResult::dispatchUpdatesTo() 根据diff 数据结果,选择刷新方式。
总结:使用起来比较简单 ,1)实现DiffUtil.Callback 接口 ;2)新老数据集通过DiffUtil.calculateDiff 计算得到DiffUtil.DiffResult ;3)DiffUtil.DiffResult::dispatchUpdatesTo() 刷新数据。
三、项目使用DiffUtil遇到的问题
在第二节中 ,已经基本介绍了DiffUtil 的使用,本节以精选页、书城等页面为例(忽略代码中边界值处理)说明 ,在使用过程中遇到的一些问题:
(1)item 判断 唯一性
目前 ,setData () 数据类型为 ArrayList<RowData> (多ViewType类型)如下 ,对于item 来说 ,缺少唯一性判断属性。
目前,使用mDisplayStyle 和id 来判断 item 是否一致。 如果item一致,则会进一步判断 item 内容是否一致(areContentsTheSame);如果item 不一致,则item 全部刷新。
(2) areContentsTheSame() 判断过于麻烦
由于RowData 内 核心数据是mData (Object型),无法直接比较 实现equals ,只能在 areContentsTheSame() 内,手动实现每种ViewType 数据的equals (对象是jce协议,只能手动实现)。
手动实现的问题在于:1)jce协议新增字段,这里需要补充 ;2)新增ViewType 数据需要在这里补充;3)对应adapter 与这个callBack 关联性不大,易漏
上述三个问题,可能增加一定的维护成本
RowDataDiffCallback() 整体实现 :
(3)VM 初始化 设置pullToRefreshRecycleView.setEnableLoadMore(true),导致页面滑到最下面
目前,猜测 是加载更多模块焦点导致 (猜测未找出原因)
分析一下该问题 :pullToRefreshRecycleView.setEnableLoadMore(true) 设置本不应该在VM初始化时,设置,应该根据回包数据 的hasMore 来设置 。
直接将pullToRefreshRecycleView.setEnableLoadMore(true) 改成在回包时,调用 pullToRefreshRecycleView.setEnableLoadMore(hasMore) 上述问题解决