Android的Kotlin秘方(II):RecyclerView 和 DiffUtil

作者:Antonio Leiva

时间: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方法。

原始实现方法

首次迭代,我们是从“提供者”那里获得这些项目,让适配器通知变化(这即使不是最好的代码,而可以很快的理解为什么这样做):

1 private fun fillAdapter() {
2     val oldItems = adapter.items
3     adapter.items = provider.generate()
4     adapter.notifyChanges(oldItems, adapter.items)
5 }

简单:我保存前面项目,产生新的项目,对适配器说notifyChanges,而用DiffUtil方法是这样:

 1 fun notifyChanges(old: List<Content>, new: List<Content>) {
 2     val diff = DiffUtil.calculateDiff(object : DiffUtil.Callback() {
 3         override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
 4             return old[oldItemPosition].id == new[newItemPosition].id
 5         }
 6
 7         override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
 8             return old[oldItemPosition] == new[newItemPosition]
 9         }
10
11         override fun getOldListSize() = old.size
12
13         override fun getNewListSize() = new.size
14     })
15
16     diff.dispatchUpdatesTo(this)
17 }

由于大部分代码都是基于模板,这实在是令人讨厌,但是稍后还是要返回了。

现在,如你所见在设置新的一组项目后我调用notifyChanges。那我们为什么不委托那些行为?

用委托使通知更简单

如果我们知道每次一组项目改变时进行通知,那么仅需要用Delegates.observer,那么代码就非常棒:

这个activity就非常简单了:

1 private fun fillAdapter() {
2     adapter.items = provider.generate()
3 }

“观察者”看上去就非常不错:

1 class ContentAdapter():RecyclerView.Adapter<ContentAdapter.ViewHolder>() {
2
3     var items: List<Content> by Delegates.observable(emptyList()) {
4         prop, old, new ->
5                 notifyChanges(old, new)
6     }
7      ...
8 }

太棒了!但是,这还可以更好。

用扩展函数提升适配器的能力

NotityChanges的大多数代码都是模式化的。如果用数据类,我们就需要实现判断两个项目是否相同的方法,即使它们的内容不同。

在这个例子中,识别的方法是id。

这样,我为这个适配器创建一个扩展函数,它将为我们做大部分困难的工作:

 1 fun <T> RecyclerView.Adapter<*>.autoNotify(old: List<T>, new: List<T>, compare: (T, T) -> Boolean) {
 2     val diff = DiffUtil.calculateDiff(object : DiffUtil.Callback() {
 3
 4         override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
 5             return compare(old[oldItemPosition], new[newItemPosition])
 6         }
 7
 8         override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
 9             return old[oldItemPosition] == new[newItemPosition]
10         }
11
12         override fun getOldListSize() = old.size
13
14         override fun getNewListSize() = new.size
15     })
16
17     diff.dispatchUpdatesTo(this)
18 }

这个函数接收两组项目,和另一个函数。这最后参数将在areItemsTheSame中使用,以确定两组项目是否相同。

现在调用是这样了:

1 var items: List<Content> by Delegates.observable(emptyList()) {
2     prop, old, new ->
3             autoNotify(old, new) { o, n -> o.id == n.id }
4 }

组合使用

我能理解你非常不喜欢前面的解决方案。而在特定情况下,你不要所有适配器都使用它。

但是,有一个替换方法:接口。

悲哀的是,在Kotlin预览上的某些位置上,接口不能扩展类(我非常希望在将来能够增加它)。这让你用扩展类的方法,强制类实现接口类型。

但是,我们将扩展函数移入接口内部,也能够获得类似的结果:

1 interface AutoUpdatableAdapter {
2
3     fun <T> RecyclerView.Adapter<*>.autoNotify(old: List<T>, new: List<T>, compare: (T, T) -> Boolean) {
4         ...
5     }
6 }

适配器只需要实现这个接口:

1 class ContentAdapter() : RecyclerView.Adapter<ContentAdapter.ViewHolder>(), AutoUpdatableAdapter {
2     ....
3 }

这就是所有代码,其它保持不变。

结论

有几个方法用DiffUtls使代码看起比Java更好、更简单。

如果你需要尝试其它解决方案,从代码库获取,并删除一些特殊注释。

时间: 2024-12-19 13:32:25

Android的Kotlin秘方(II):RecyclerView 和 DiffUtil的相关文章

Android的Kotlin秘方(I):OnGlobalLayoutListener

春节后,又重新“开张”.各位高手请继续支持.谢谢! 原文标题:Kotlin recipes for Android (I): OnGlobalLayoutListener 原文链接:http://antonioleiva.com/kotlin-ongloballayoutlistener/ 原文作者:Antonio Leiva(http://antonioleiva.com/about/) 原文发布:2016-03-16 ­ 今天一位同伴问我怎样恰当使用OnGlobalLayoutListene

Google Android Studio Kotlin

Google Android Studio Kotlin 开发环境配置 Google 近日开发者大会宣布Kotlin成为Android开发的第一级语言,即Android官方开发语言,可见Google对Kotlin的重视,本文就介绍一下Android Studio下的Kotlin开发环境配置以及示例程序. 由于楼主是C++程序员,不是Java出身,也不太懂移动端的Android开发,业余时间想学习Android移动端开发,但是楼主又不想学Java,该如何是好呢(O(∩_∩)O哈哈~),正好Kotl

Android开发学习之路-RecyclerView滑动删除和拖动排序

Android开发学习之路-RecyclerView使用初探 Android开发学习之路-RecyclerView的Item自定义动画及DefaultItemAnimator源码分析 Android开发学习之路-下拉刷新怎么做? 本篇是接着上面三篇之后的一个对RecyclerView的介绍,这里多说两句,如果你还在使用ListView的话,可以放弃掉ListView了.RecyclerView自动帮我们缓存Item视图(ViewHolder),允许我们自定义各种动作的动画和分割线,允许我们对It

【Android 界面效果47】RecyclerView详解

RecylerView作为 support-library发布出来,这对开发者来说绝对是个好消息.因为可以在更低的Android版本上使用这个新视图.下面我们看如何获取 RecylerView.首先打开Android SDK Manager,然后更新Extras->Android Support Library即可. 然后在本地../sdk/extras/android/support/v7中找到recyclerview.我已经将下载好的Recyclerview整理成一个Eclipse可编译的L

android L新控件RecyclerView详解与DeMo

介绍 在谷歌的官网我们可以看到它是这样介绍的:RecyclerView is a more advanced and flexible version of ListView. This widget is a container for large sets of views that can be recycled and scrolled very efficiently. Use the RecyclerView widget when you have lists with eleme

基于Android官方AsyncListUtil优化改进RecyclerView分页加载机制(一)

基于Android官方AsyncListUtil优化改进RecyclerView分页加载机制(一) Android AsyncListUtil是Android官方提供的专为列表这样的数据更新加载提供的异步加载组件.基于AsyncListUtil组件,可以轻易实现常见的RecyclerView分页加载技术.AsyncListUtil技术涉及的细节比较繁复,因此我将分别写若干篇文章,分点.分解AsyncListUtil技术. 先给出一个可运行的例子,MainActivity.java: packag

基于Android官方Paging Library的RecyclerView分页加载框架

基于Android官方Paging Library的RecyclerView分页加载框架 我之前写了一篇RecyclerView分页加载机制的文章,是基于Android官方的AsyncListUtil实现的,详情见附录文章1.现在再介绍一种RecyclerView分页加载框架:Android Paging Library.Android Paging Library是Android官方support-v7支持包中专门做的分页框架,详细文档见谷歌官方文档附录2页面.我写这篇文章时候Paging L

Android Material Design学习之RecyclerView代替 ListView

前言 # Android Material Design越来越流行,以前很常用的 ListView 现在也用RecyclerView代替了,实现原理还是相似的.笔者实现一下 RecyclerView,代码比较简单,适合初学者,如有错误,欢迎指出. 源码地址(欢迎star) https://github.com/studychen/SeeNewsV2 本文链接 http://blog.csdn.net/never_cxb/article/details/50495505,转载请注明出处. 复习 L

Android Material Design之在RecyclerView中嵌套CardView实现

前言: 第一眼就爱上了Android的Material Design风格.以前倒对Android的界面风格不那么喜欢,扁平化的界面设计真是好看. 其实,这个嵌套操作在实现上并没有什么难点.可是,我还在Eclipse上没有试验成功.后来换到Android Studio上就OK了.以下是实现过程. 笔者开发环境: 系统:Windows 7 旗舰版 IDE:Android Studio v1.0 工具包:cardview-v7-21.0.0.aar recyclerview-v7-21.0.0.aar