RecyclerView 配合 DiffUtil,好用到飞起
版权声明:
本账号发布文章均来自公众号,承香墨影(cxmyDev),版权归承香墨影所有。
每周会统一更新到这里,如果喜欢,可关注公众号获取最新文章。
未经允许,不得转载。
一、前言
DiffUtils 是 Support-v7:24:2.0 中,更新的工具类。因为已经更新了一段时间了,也不好说是最新更新的。
它主要是为了配合 RecyclerView 使用,通过比对新、旧两个数据集的差异,生成旧数据到新数据的最小变动,然后对有变动的数据项,进行局部刷新。
接下来就 DiffUtil 的使用细节,进行一个详细的讲解,希望一篇文章就完全理解 DiffUtil。
二、为什么会有DiffUtil
RecyclerView 自从被发布以来,一直被说成是 ListView、GridView 等一系列列表控件的完美替代品。并且它本身使用起来也非常的好用,布局切换方便、自带 ViewHolder 、局部更新并且可带更新动画等等。
局部更新、并且可以很方便的设置更新动画这一点,是 RecyclerView 一个不错的亮点。它为此提供了对应的方法:
- adapter.notifyItemChange()
- adapter.notifyItemInserted()
- adapter.notifyItemRemoved()
- adapter.notifyItemMoved();
以上方法都是为了对数据集中,单一项进行操作,并且为了操作连续的数据集的变动,还提供了对应的 notifyRangeXxx()
方法。
虽然 RecyclerView 提供的局部更新的方法,看似非常的好用,但是实际上,其实并没有什么用。
在实际开发中,最方便的做法就是无脑调用 notifyDataSetChanged()
,用于更新 Adapter 的数据集。
虽然 notifyDataSetChanged()
有一些缺点:
- 不会触发 RecyclerView 的局部更新的动画。
- 性能低,会刷新整个 RecyclerView 可视区域。
但是真有需要频繁刷新,前后两个数据集的场景。
方案一:使用一个 notifyDataSetChanged()
方法。
方案二:自己写一个数据集比对方法,然后去计算他们的差值,最后调用对应的方法更新到 RecyclerView 中去。
我这么懒,如果不是必要,当然是会选 方案一 了。毕竟和之前 ListView 的时候,也没有更差了。
Google 显然也发现了这个问题,所以 DiffUtil 被发布了。
三、介绍DiffUtil
就像前面说的,DiffUtil 就是为了解决这个痛点的。它能很方便的对两个数据集之间进行比对,然后计算出变动情况,配合 RecyclerView.Adapter ,可以自动根据变动情况,调用 Adapter 的对应方法。
当然,DiffUtil 不仅只能配合 RecyclerView 使用,它实际上可以单独用于比对两个数据集,然后如何操作是可以定制的,那么在什么场景下使用,就全凭我们自己发挥了。
DiffUtil 在使用起来,主要需要关注几个类:
- DiffUtil.Callback:具体用于限定数据集比对规则。
- DiffUtil.DiffResult:比对数据集之后,返回的差异结果。
1、DiffUtil.Callback
DiffUtil.Callback 主要就是为了限定两个数据集中,子项的比对规则。毕竟开发者面对的数据结构多种多样,既然没法做一套通用的内容比对方式,那么就将比对的规则,交还给开发者来实现即可。
在 Callback 中,其实只需要实现 4 个方法:
getOldListSize()
:旧数据集的长度。getNewListSize()
:新数据集的长度areItemsTheSame()
:判断是否是同一个Item。areContentsTheSame()
:如果是通一个Item,此方法用于判断是否同一个 Item 的内容也相同。
前两个是获取数据集长度的方法,这没什么好说的。但是后两个方法,主要是为了对应多布局的情况产生的,也就是存在多个 viewType 和多个 ViewHodler 的情况。首先需要使用 areItemsTheSame()
方法比对是否来自同一个 viewType(也就是同一个 ViewHolder ) ,然后再通过 areContentsTheSame()
方法比对其内容是否也相等。
其实 Callback 还有一个 getChangePayload()
的方法,它可以在 ViewType 相同,但是内容不相同的时候,用 payLoad 记录需要在这个 ViewHolder 中,具体需要更新的View。
areItemsTheSame()
、areContentsTheSame()
、getChangePayload()
分别代表了不同量级的刷新。
首先会通过 areItemsTheSame()
判断当前 position 下,ViewType 是否一致,如果不一致就表明当前 position 下,从数据到 UI 结构上全部变化了,那么就不关心内容,直接更新就好了。如果一致的话,那么其实 View 是可以复用的,就还需要再通过 areContentsTheSame()
方法判断其内容是否一致,如果一致,则表示是同一条数据,不需要做额外的操作。但是一旦不一致,则还会调用 getChangePayload()
来标记到底是哪个地方的不一样,最终标记需要更新的地方,最终返回给 DiffResult 。
当然,对性能要是要求没那么高的情况下,是可以不使用 getChangedPayload()
方法的。
2、DiffUtil.DiffResult
DiffUtil.DiffResult 其实就是 DiffUtil 通过 DiffUtil.Callback 计算出来,两个数据集的差异。它是可以直接使用在 RecyclerView 上的。如果有必要,也是可以通过实现 ListUpdateCallback 接口,来比对这些差异的。
3、使用DiffUtil
介绍了 Callback 和 DiffResult 之后,其实就可以正常使用 DiffUtil 来进行数据集的比对了。
在这个过程中,其实真的很简单,只需要调用两个方法:
DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new DiffCallBack(oldDatas, newDatas), true);
diffResult.dispatchUpdatesTo(mAdapter);
calculateDiff 方法主要是用于通过一个具体的 DiffUtils.Callback 实现对象,来计算出两个数据集差异的结果,得到 DiffUtil.DiffResult 。
而 calculateDiff 的另外一个参数,用于标记是否需要检测 Item 的移动。
DiffUtil 使用的是 Eugene Myers 的差别算法,这个算法本身是不检查元素的移动的。也就是说,有元素的移动它也只是会先标记为删除,然后再标记插入。而如果需要计算元素的移动,它实际上也是在通过 Eugene Myers 算法比对之后,再进行一次移动检查。所以,如果集合本身已经排序过了,可以不进行移动的检查。
而 dispatchUpdatesTo()
就是将这个数据集差异的结果,通过 Adapter 更新到 RecyclerView 上面。
实际上 dispatchUpdatesTo(Adapter)
,也是使用的 ListUpdateCallback 这个接口,在其中获得差异,然后调用 Adapter 的对应方法。
四、上例子
既然已经说清楚了,那么我们开始上例子了。
功能很简单,有四个数据集,使用 RecyclerView 承载,然后有一个按钮,用于轮换的切换数据集。
1、实现 DiffUtil.Callback
为了简单,RecyclerView 中使用单一 ViewType ,并且使用一个 TextView 承载一个 字符串来显示。
那么我们开始实现 Callback:
2、切换数据集
既然已经有了 DiffUtil.Callback 的实现之后,我们就需要对切换数据集的点击事件进行处理了。
3、实现效果
关键代码已经贴出来了,其实非常的简单,最终运行的效果如下:
五、DiffUtil 效率问题
既然 DiffUtil 非常的好用,并且内部也实现了一套算法,但是我们也需要关心它的效率问题。
根据 Google 官方文档中给出的例子,在 Nexus 5X M 系统上,DiffUtil 的效率问题,给出了一些参考的数据:
可以看到,实际上,DiffUtil 的算法把效率问题解决的非常的好。在开启计算移动的情况下,1000 条数据中有 200 个修改,平均值也只有 13.54 ms ,基本上都是毫秒级的。
Google 官方同时也指出,如果是对大数据集的比对,最好是方在子线程中去完成计算,也就是其实是存在堵塞 UI 的情况的。所以如果你遇见了使用 DiffUtil 之后,每次刷新有卡顿的情况,可以考虑是否数据集太大,是否应该在子线程中完成计算。
六、结语
DiffUtil 已经介绍完了,如果觉得本文对你有帮助。都看到这里了,点个赞再走吧。
RecyclerView 配合 DiffUtil,好用到飞起的更多相关文章
- Android的Kotlin秘方(II):RecyclerView 和 DiffUtil
作者:Antonio Leiva 时间:Sep 12, 2016 原文链接:http://antonioleiva.com/recyclerview-diffutil-kotlin/ 如你所知,在[支 ...
- Android局部更新(RecyclerView+ DiffUtil)
一 概述 DiffUtil是support-v7:24.2.0中的新工具类,它用来比较两个数据集,寻找出旧数据集->新数据集的最小变化量. 说到数据集,相信大家知道它是和谁相关的了,就是我的最爱 ...
- 【RecyclerView优化】
1.局部刷新 (1)避免整个列表的数据更新,只更新受影响的布局.例如,加载更多时,不使用notifyDataSetChanged(),而是使用notifyItemRangeInserted(range ...
- Android MultiType第三方库的基本使用和案例+DiffUtil的简单用法
1.MultiType简单介绍 1.1.MultiType用于比较复杂的页面. 如下图,今日头条用到了MultiType处理各种复杂的页面. 这种还是比较简单的类型.因为一个页面也就这种类型. ...
- (转载)详解7.0带来的新工具类:DiffUtil
[Android]详解7.0带来的新工具类:DiffUtil 标签: diffutil 2017-04-17 18:21 226人阅读 评论(0) 收藏 举报 分类: Android学习笔记(94) ...
- Android滑动列表(拖拽,左滑删除,右滑完成)功能实现(1)
场景: 近期做的TODO APP需要在主页添加一个功能,就是可以左滑删除,右滑完成.看了一下当前其他人做的例如仿探探式的效果,核心功能基本一样,但是和我预想的还是有少量区别,于是干脆自己重头学一遍如何 ...
- 如何正确使用Espresso来测试你的Android程序
UI测试在Android平台上一直都是一个令人头痛的事情, 由于大家平时用的很少, 加之很多文档的缺失, 如果很多东西从头摸索,势必踩坑无数. 自Android24正式淘汰掉了Instrumentat ...
- 注解:大话AOP与Android的爱恨情仇
转载:大话AOP与Android的爱恨情仇 1. AOP与OOP的区别 平时我接触多的就是OOP(Object Oriented Programming面向对象).AOP(Aspect Oriente ...
- [Android Pro] AndroidX重构和映射
原文地址:https://developer.android.com/topic/libraries/support-library/refactor https://blog.csdn.net/ch ...
随机推荐
- JAVAEE——SSH项目实战01:SVN介绍、安装和使用方法
1 学习目标 1.掌握svn服务端.svn客户端.svn eclipse插件安装方法 2.掌握svn的基本使用方法 2 svn介绍 2.1 项目管理中的版本控制问题 通常软件开发由多人协作开发,如果对 ...
- react系列从零开始-react介绍
react算是目前最火的js MVC框架了,写一个react系列的博客,顺便回忆一下react的基础知识,新入门前端的小白,可以持续关注,我会从零开始教大家用react开发一个完整的项目,也会涉及到w ...
- 2017寒假零基础学习Python系列之函数之 递归函数
什么是递归函数? 在函数内部,也可以继续调用其他函数,如果一个函数在内部调用本身,这个函数为递归函数举一个求n的阶乘的例子: def fact(n): if n == 1: return 1; els ...
- 深度学习:Keras入门(一)之基础篇
1.关于Keras 1)简介 Keras是由纯python编写的基于theano/tensorflow的深度学习框架. Keras是一个高层神经网络API,支持快速实验,能够把你的idea迅速转换为结 ...
- vijos1056题解
题目: 桌面上放了N个平行于坐标轴的矩形,这N个矩形可能有互相覆盖的部分,求它们组成的图形的面积. 在翻题目时,偶然发现了这道标号为WA的题目. 原来,以前我把一中培训的代码发了上去,却WA了4个点, ...
- DotNetCore跨平台~服务总线_事件总线的重新设计
理论闲话 之前在.netFramework平台用的好好的,可升级到.net core平台之后,由于不再需要二进制序列化,导致咱们的事件机制遇到了问题,之前大叔的事件一直是将处理程序序列化后进行存储的, ...
- Gist - ES6 Proxy
Introduction "Proxy" is a frequently used pattern in both virtual world and real world. Th ...
- epii.js简约而不简单的JS模板引擎
epii.js是什么 epii.js是一个 模板引擎,可快速实现数据与ui绑定,快速实现事件绑定,与处理,不依赖任何第三方库,仅仅8k,在native+webapp开发 和 web开发,h5微网页上均 ...
- QuartusII 13.0 PLL IP Core调用及仿真
有一个多月没用用Quartus II了,都快忘了IP 是怎么用调用的了,还好有之前做的笔记,现在整理出来,终于体会到做笔记的好处. 一. QuartusII的pll的调用 打开软件界面 Tool—— ...
- 使用xcrun打包iOS应用
使用xcrun打包iOS应用 通常打包采用xcodebuild和xcrun两个命令,xcodebuild负责编译,xcrun负责将app打成ipa. XCode 默认编译出来的是appName.a ...