作者: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更好、更简单。

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

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

  1. Android的Kotlin秘方(I):OnGlobalLayoutListener

    春节后,又重新“开张”.各位高手请继续支持.谢谢! 原文标题:Kotlin recipes for Android (I): OnGlobalLayoutListener 原文链接:http://an ...

  2. android data binding jetpack II 动态数据更新

    android data binding jetpack VIII BindingConversion android data binding jetpack VII @BindingAdapter ...

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

    Android开发学习之路-RecyclerView使用初探 Android开发学习之路-RecyclerView的Item自定义动画及DefaultItemAnimator源码分析 Android开 ...

  4. [Android]使用Kotlin开发Android(二)

    以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/4829007.html [TOC] 使用Kotlin+OkHtt ...

  5. Google Android Studio Kotlin 开发环境配置

    Google 近日开发者大会宣布Kotlin成为Android开发的第一级语言,即Android官方开发语言,可见Google对Kotlin的重视,本文就介绍一下Android Studio下的Kot ...

  6. 【Android 界面效果47】RecyclerView详解

    RecylerView作为 support-library发布出来,这对开发者来说绝对是个好消息.因为可以在更低的Android版本上使用这个新视图.下面我们看如何获取 RecylerView.首先打 ...

  7. Google Android Studio Kotlin

    Google Android Studio Kotlin 开发环境配置 Google 近日开发者大会宣布Kotlin成为Android开发的第一级语言,即Android官方开发语言,可见Google对 ...

  8. Android Studio(Kotlin)之RecyclerView

    RecyclerView应该是ListView的增强版. RecyclerView与ListView的区别(我认为的): RecyclerView的性能比ListView高 RecyclerView支 ...

  9. Android开发学习之路-RecyclerView使用初探

    在进行一些MaterialDesign规范开发的时候,比如之前说到的CoordinateLayout实现的向上折叠效果的时候,如果依然使用ListView,那么这种效果是做不出来的,因为ListVie ...

随机推荐

  1. C# DateTime与时间戳转换

    C# DateTime与时间戳的相互转换,包括JavaScript时间戳和Unix的时间戳. 1. 什么是时间戳 首先要清楚JavaScript与Unix的时间戳的区别: JavaScript时间戳: ...

  2. Asp.Net Mvc 使用WebUploader 多图片上传

    来博客园有一个月了,哈哈.在这里学到了很多东西.今天也来试着分享一下学到的东西.希望能和大家做朋友共同进步. 最近由于项目需要上传多张图片,对于我这只菜鸟来说,以前上传图片都是直接拖得控件啊,而且还是 ...

  3. 工欲善其事,必先利其器 之 VS2013全攻略(安装,技巧,快捷键,插件)!

    如有需要WPF工具的朋友可以移步 工欲善其事,必先利其器 之 WPF篇: 随着开发轨迹来看高效WPF开发的工具和技巧 之前一篇<c++的性能, c#的产能?!鱼和熊掌可以兼得,.NET NATI ...

  4. 三分钟学会用 js + css3 打造酷炫3D相册

    之前发过该文,后来不知怎么回事不见了,现在重新发一下. 中秋主题的3D旋转相册 如图,这是通过Javascript和css3来实现的.整个案例只有不到80行代码,我希望通过这个案例,让正处于迷茫期的j ...

  5. Android 获取系统相册中的所有图片

    Android 提供了API可获取到系统相册中的一些信息,主要还是通过ContentProvider 来获取想要的内容. 代码很简单,只要熟悉ContentProvider 就可以了. public ...

  6. HTTP常用状态码分析

    不管是面试还是工作中,经常会碰到需要通过HTTP状态码去判断问题的情况,比如对于后台RD,给到前端FE的一个接口,出现502或者504 error错误,FE就会说接口存在问题,如果没有知识储备,那就只 ...

  7. 【夯实Mysql基础】MySQL性能优化的21个最佳实践 和 mysql使用索引

    本文地址 分享提纲: 1.为查询缓存优化你的查询 2. EXPLAIN 你的 SELECT 查询 3. 当只要一行数据时使用 LIMIT 1 4. 为搜索字段建索引 5. 在Join表的时候使用相当类 ...

  8. Android之SQLite数据存储

    一.SQLite保存数据介绍 将数据库保存在数据库对于重复或者结构化数据(比如契约信息)而言是理想之选.SQL数据库的主要原则之一是架构:数据库如何组织正式声明.架构体现于用于创建数据库的SQL语句. ...

  9. Configure a bridge interface over a VLAN tagged bonded interface

    SOLUTION VERIFIED February 5 2014 KB340153 Environment Red Hat Enterprise Linux 6 (All Versions) Red ...

  10. Android(2)—Mono For Android App版本自动更新

    0.前言 公司Android查询的项目已经开始,整体采用Java后台+App前台来实现,由于项目小,App这块就我一个人开发,首先需要研究的是:Android版本的更新升级问题:本人经过近一周的学习整 ...