文章目录
- 基础复习:Kotlin语言中的对象比较
- 背景
- 问题出现
- 解决方式
- 方式一
- 方式二
基础复习:Kotlin语言中的对象比较
比较对象的内容是否相等 (== 或者 equals )
:Kotlin 中的操作符 == 和 equals效果相同 ,都用于比较对象的内容是否相等, Kotlin中建议直接使用 ==。比较对象的引用是否相等 ( === )
:Kotlin 中的操作符 === 用于比较对象的引用是否指向同一个地址,运行时如果是基本数据类型 === 等价于 ==。
背景
如图效果,通过RecyclerView实现,每次通过对每个Item前后数据进行对比来确定执行什么操作(如Item的insert、update、delete等),这里使用RecyclerView库中的DiffUtil.Callback()来进行的前后数据对比,如下示例:
class DataDiffUtil(private val oldModels: List<Any>, private val newModels: List<Any>) :DiffUtil.Callback() {/*** 旧数据*/override fun getOldListSize(): Int = oldModels.size/*** 新数据*/override fun getNewListSize(): Int = newModels.size/*** DiffUtil调用来决定两个对象是否代表相同的Item。true表示两个Item相同(表示View可以复用),false表示不相同(View不可以复用)* 例如,如果你的项目有唯一的id,这个方法应该检查它们的id是否相等。*/override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {return oldModels[oldItemPosition]::class.java == newModels[newItemPosition]::class.java}/*** 比较两个Item是否有相同的内容(用于判断Item的内容是否发生了改变),* 该方法只有当areItemsTheSame (int, int)返回true时才会被调用。*/override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {return oldModels[oldItemPosition] == newModels[newItemPosition]}/*** 该方法执行时机:areItemsTheSame(int, int)返回true 并且 areContentsTheSame(int, int)返回false* 该方法返回Item中的变化数据,用于只更新Item中变化数据对应的UI*/override fun getChangePayload(oldItemPosition: Int, newItemPosition: Int): Any? {return super.getChangePayload(oldItemPosition, newItemPosition)}
}
以顶部的Item1 模块举例,当服务端有新数据来时,通过下面方式进行更新:
/*** use[DiffUtil] 增量更新数据* @param newList 新数据*/
fun submitList(newList: MutableList<Any>) {//传入新旧数据进行比对val diffUtil = DataDiffUtil(mModels, newList)//经过比对得到差异结果val diffResult = DiffUtil.calculateDiff(diffUtil)//NOTE:注意这里要重新设置Adapter中的数据setModels(newList)//将数据传给adapter,最终通过adapter.notifyItemXXX更新数据diffResult.dispatchUpdatesTo(this)
}
如果Item1
前后数据是一样的,那么DiffUtil.Callback#areContentsTheSame()
中的oldModels[oldItemPosition] == newModels[newItemPosition]
理论上返回的就是true,Item1 模块也不会执行刷新操作了。
实际跑起来能按我们的预期走吗?
问题出现
上述逻辑写的差不多了,还差Model
数据类没有写出来,因为项目中是Kotlin & Java
混用的,而Model
数据类正好是用Java语言编写的:
public class VP2Model implements Serializable {public int id;public String content;
}
看上去一切都是OK的,但是运行之后发现出问题了,即使前后数据完全一样,仍然会进行Item1
的刷新,说明DiffUtil.Callback#areContentsTheSame()
里的数据对比返回的是false
,通过断点发现确实返回了false。
到这里不知道大家有没有发现问题所在?开始以为是数据变了,但是通过Log打点发现前后数据是一样的,那么明明是一样的,为什么对比会是不同呢?仔细一想明白了,问题出在Java语言上,出在VP2Model类中没有重新equals()方法:
@Override
public boolean equals(@Nullable Object obj) {return super.equals(obj);
}
Java Model
类默认的equals()
方法是比较的对象内存地址,刷新前后生成的显然不是同一个对象,那么前后地址对比返回的肯定是false了,问题就出在了这里!
如果我们使用 Kotlin 语言编写 Model 类就不会有这个问题,因为 Kotlin 编译器自动帮我们重写了equals()/hashCode()
方法,如:
data class VP2Model(val id: Int = 0,val content: String = "",
)
注意这里要用data class
开头才行,上述代码转换成Java
后:
可以看到 Kotlin 编写的 Model 类自动帮我们实现了其中的equals()/hashCode()
方法。
解决方式
已经知道问题出现的原因,那么解决方式就很简单了,比如下面几种解决方式:
方式一
重写Java Model
类中的 equals()
方法,对每个字段进行对比,字段都相同返回 true,否则返回 false。
有一种快捷生成方式,在Mac
版的 AS 中,可以使用 command + N 的方式生成,如下:
生成结果:
方式二
在使用的地方用 Kotlin
语言编写Model
类进行转换,注意:这里一定要用data class
开头的声明,因为 Kotlin
编译器会自动帮我们重写 equals()/hashCode()
方法。