Android的Kotlin秘方(II):RecyclerView 和 DiffUtil
时间:Sep 12, 2016
原文链接:http://antonioleiva.com/recyclerview-diffutil-kotlin/
如你所知,在【支持库24(the Support Library 24)】中包括一个新的、适用、方便的类:DiffUtil,这使你摆脱对单元改变和更新它们的无聊和易出错。
如果你还不了解它,可以阅读Nicola Despotoski的这篇好文章了解它。这篇文章解释怎样容易处理它。
实际上,Java语言引入许多模板,而我决定研究是用Kotlin怎样实现。
例子
我创建一个小APP(可以在GitHub下载)作为例子,它从一个有10项的列表中选择项目,用于下一次RecycerView。

这样,从一次迭代到下次,有些被显示,有些消失,而有时整个全部更新。
如果你知道RecyclerView是怎样工作的,你就知道在它的适配器怎样通知那些改变,这就需要这三个方法:
- notifyItemChanged
- notifyItemInserted
- notifyItemRemoved
以及它们对应的Range变化。
DiffUtil类将我们做所有的计算,且调用要求的notify方法。
原始实现方法
首次迭代,我们是从“提供者”那里获得这些项目,让适配器通知变化(这即使不是最好的代码,而可以很快的理解为什么这样做):
private fun fillAdapter() {
val oldItems = adapter.items
adapter.items = provider.generate()
adapter.notifyChanges(oldItems, adapter.items)
}
简单:我保存前面项目,产生新的项目,对适配器说notifyChanges,而用DiffUtil方法是这样:
fun notifyChanges(old: List<Content>, new: List<Content>) {
val diff = DiffUtil.calculateDiff(object : DiffUtil.Callback() {
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return old[oldItemPosition].id == new[newItemPosition].id
}
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return old[oldItemPosition] == new[newItemPosition]
}
override fun getOldListSize() = old.size
override fun getNewListSize() = new.size
})
diff.dispatchUpdatesTo(this)
}
由于大部分代码都是基于模板,这实在是令人讨厌,但是稍后还是要返回了。
现在,如你所见在设置新的一组项目后我调用notifyChanges。那我们为什么不委托那些行为?
用委托使通知更简单
如果我们知道每次一组项目改变时进行通知,那么仅需要用Delegates.observer,那么代码就非常棒:
这个activity就非常简单了:
private fun fillAdapter() {
adapter.items = provider.generate()
}
“观察者”看上去就非常不错:
class ContentAdapter():RecyclerView.Adapter<ContentAdapter.ViewHolder>() {
var items: List<Content> by Delegates.observable(emptyList()) {
prop, old, new ->
notifyChanges(old, new)
}
...
}
太棒了!但是,这还可以更好。
用扩展函数提升适配器的能力
NotityChanges的大多数代码都是模式化的。如果用数据类,我们就需要实现判断两个项目是否相同的方法,即使它们的内容不同。
在这个例子中,识别的方法是id。
这样,我为这个适配器创建一个扩展函数,它将为我们做大部分困难的工作:
fun <T> RecyclerView.Adapter<*>.autoNotify(old: List<T>, new: List<T>, compare: (T, T) -> Boolean) {
val diff = DiffUtil.calculateDiff(object : DiffUtil.Callback() {
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return compare(old[oldItemPosition], new[newItemPosition])
}
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return old[oldItemPosition] == new[newItemPosition]
}
override fun getOldListSize() = old.size
override fun getNewListSize() = new.size
})
diff.dispatchUpdatesTo(this)
}
这个函数接收两组项目,和另一个函数。这最后参数将在areItemsTheSame中使用,以确定两组项目是否相同。
现在调用是这样了:
var items: List<Content> by Delegates.observable(emptyList()) {
prop, old, new ->
autoNotify(old, new) { o, n -> o.id == n.id }
}
组合使用
我能理解你非常不喜欢前面的解决方案。而在特定情况下,你不要所有适配器都使用它。
但是,有一个替换方法:接口。
悲哀的是,在Kotlin预览上的某些位置上,接口不能扩展类(我非常希望在将来能够增加它)。这让你用扩展类的方法,强制类实现接口类型。
但是,我们将扩展函数移入接口内部,也能够获得类似的结果:
interface AutoUpdatableAdapter {
fun <T> RecyclerView.Adapter<*>.autoNotify(old: List<T>, new: List<T>, compare: (T, T) -> Boolean) {
...
}
}
适配器只需要实现这个接口:
class ContentAdapter() : RecyclerView.Adapter<ContentAdapter.ViewHolder>(), AutoUpdatableAdapter {
....
}
这就是所有代码,其它保持不变。
结论
有几个方法用DiffUtls使代码看起比Java更好、更简单。
如果你需要尝试其它解决方案,从代码库获取,并删除一些特殊注释。
Android的Kotlin秘方(II):RecyclerView 和 DiffUtil的更多相关文章
- Android的Kotlin秘方(I):OnGlobalLayoutListener
春节后,又重新“开张”.各位高手请继续支持.谢谢! 原文标题:Kotlin recipes for Android (I): OnGlobalLayoutListener 原文链接:http://an ...
- android data binding jetpack II 动态数据更新
android data binding jetpack VIII BindingConversion android data binding jetpack VII @BindingAdapter ...
- Android开发学习之路-RecyclerView滑动删除和拖动排序
Android开发学习之路-RecyclerView使用初探 Android开发学习之路-RecyclerView的Item自定义动画及DefaultItemAnimator源码分析 Android开 ...
- [Android]使用Kotlin开发Android(二)
以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/4829007.html [TOC] 使用Kotlin+OkHtt ...
- Google Android Studio Kotlin 开发环境配置
Google 近日开发者大会宣布Kotlin成为Android开发的第一级语言,即Android官方开发语言,可见Google对Kotlin的重视,本文就介绍一下Android Studio下的Kot ...
- 【Android 界面效果47】RecyclerView详解
RecylerView作为 support-library发布出来,这对开发者来说绝对是个好消息.因为可以在更低的Android版本上使用这个新视图.下面我们看如何获取 RecylerView.首先打 ...
- Google Android Studio Kotlin
Google Android Studio Kotlin 开发环境配置 Google 近日开发者大会宣布Kotlin成为Android开发的第一级语言,即Android官方开发语言,可见Google对 ...
- Android Studio(Kotlin)之RecyclerView
RecyclerView应该是ListView的增强版. RecyclerView与ListView的区别(我认为的): RecyclerView的性能比ListView高 RecyclerView支 ...
- Android开发学习之路-RecyclerView使用初探
在进行一些MaterialDesign规范开发的时候,比如之前说到的CoordinateLayout实现的向上折叠效果的时候,如果依然使用ListView,那么这种效果是做不出来的,因为ListVie ...
随机推荐
- 浅谈 Fragment 生命周期
版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 源码:AndroidDemo/Fragment 文中如有纰漏,欢迎大家留言指出. Fragment 是在 Android 3.0 中 ...
- 理解nodejs模块的scope
描述 原文档地址:https://docs.npmjs.com/misc/scope 所有npm模块都有name,有的模块的name还有scope.scope的命名规则和name差不多,同样不能有ur ...
- $ORACLE_HOME变量值末尾多“/”惹的祸
之前一直误以为$ORACLE_HOME变量的路径中末尾多写一个"/"不会有影响. 今天做实验时碰到一个情景,发现并不是这样. 环境:OEL 5.7 + Oracle 10.2.0. ...
- 最好的.NET开源免费ZIP库DotNetZip(.NET组件介绍之三)
在项目开发中,除了对数据的展示更多的就是对文件的相关操作,例如文件的创建和删除,以及文件的压缩和解压.文件压缩的好处有很多,主要就是在文件传输的方面,文件压缩的好处就不需要赘述,因为无论是开发者,还是 ...
- Unity3D 5.3 新版AssetBundle使用方案及策略
1.概览 Unity3D 5.0版本之后的AssetBundle机制和之前的4.x版本已经发生了很大的变化,一些曾经常用的流程已经不再使用,甚至一些老的API已经被新的API所取代. 因此,本文的主要 ...
- Log4net - 规则简介
参考页面: http://www.yuanjiaocheng.net/CSharp/csharprumenshili.html http://www.yuanjiaocheng.net/entity/ ...
- Lind.DDD.Aspects通过Plugins实现方法的动态拦截~Lind里的AOP
回到目录 .Net MVC之所以发展的如些之好,一个很重要原因就是它公开了一组AOP的过滤器,即使用这些过滤器可以方便的拦截controller里的action,并注入我们自己的代码逻辑,向全局的异常 ...
- PHP设计模式(四)单例模式(Singleton For PHP)
今天讲单例设计模式,这种设计模式和工厂模式一样,用的非常非常多,同时单例模式比较容易的一种设计模式. 一.什么是单例设计模式 单例模式,也叫单子模式,是一种常用的软件设计模式.在应用这个模式时,单例对 ...
- 敏捷转型历程 - Sprint3 一团糟的演示会
我: Tech Leader 团队:团队成员分布在两个城市,我所在的城市包括我有4个成员,另外一个城市包括SM有7个成员.另外由于我们的BA离职了,我暂代IT 的PO 职位.PM和我在一个城市,但他不 ...
- 前端开发小白必学技能—非关系数据库又像关系数据库的MongoDB快速入门命令(2)
今天给大家道个歉,没有及时更新MongoDB快速入门的下篇,最近有点小忙,在此向博友们致歉.下面我将简单地说一下mongdb的一些基本命令以及我们日常开发过程中的一些问题.mongodb可以为我们提供 ...