RecyclerView — DiffUtil
DiffUtil 16年出来的,是为了我饿们你在更新列表数据时可以实现动画效果
样子是这样的:
基础部分我就不写了,大家看这里就行,写的挺清楚:
本文例子:
接下来我就来说说注意事项
1. DiffUtil 动画效果
DiffUtil 想出动画效果,必须给 RecyclerView 添加 item 动画,item 动画的默认是先类是 DefaultItemAnimator,并且 add,remove,update,move可以分贝设置时间
var itemAnimator = DefaultItemAnimator()
itemAnimator.addDuration = 3000
itemAnimator.removeDuration = 3000
itemAnimator.changeDuration = 3000
itemAnimator.moveDuration = 3000
recy_diff.itemAnimator = itemAnimator
默认的 item 动画一般,推荐大家自己去做自己的效果
2. DiffUtil 动画的执行顺序
我这里为了明显,每个动画是 3秒
DiffUtil 动画的执行顺序:
- 先 remove
- 再同时 move 和 update
- 最后 add
3. 4种动画对应什么操作
我们只有知道自己的数据怎么变化,才能猜测出动画执行的轮廓,然后调整不是
DiffUtil 对对比新旧2个集合对应 position 位置 item
- 先找新旧2个集合有没有一样的 item ,也就是 areContentsTheSame = true 的,然后计算位置,执行 move 动画
- 在第一步的前提下,对比相同 position 位置是不是同一个,也就是 item areItemsTheSame = false ,执行 remove 动画
- 相同 position item ,areItemsTheSame = true,areContentsTheSame = false 的执行 updata 动画
- 最后多出来位置的 item 执行 add 动画
4. areItemsTheSame 方法意义
areItemsTheSame 需要特别注意,areItemsTheSame 书写不正当,非常容易触发系统 bug
java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid view holder adapter positionViewHolder{431a7450 position=1 id=-1, oldPos=-1, pLpos:-1 scrap [attachedScrap] tmpDetached no parent}
这个 bug 是系统抛出来的,无解,我们处理不了, 根本原因在于 areItemsTheSame 我们没处理好
areItemsTheSame 方法的目的是:判断新旧2个列表相同 positon 位置的 item 是否是同一个,这个不光是判断 item 是不是同一个 itemtype,更要判断这个 item 的身份是不是同一个,就好比时同一个人,比如这个人的衣服,前一分钟是绿色的,然后变成了红色,目的就是这样,看是不是同一个 item, 同一个 item 有可能默写数据有变化,我们在 areContentsTheSame 方法再去判断数据是不是有变化,有变化更新数据
- 若是同一个人,内容也没变化,那么这个 item 就会做 move
- 若是同一个人,但内容有变化,那么在 areContentsTheSame 里面判断并做内容更新
- 若不是同一个人,那么做 remove
我的例子是这样写的:
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
if (!oldData.get(oldItemPosition).javaClass.equals(newData.get(newItemPosition).javaClass)) return false
if (oldData.get(oldItemPosition) is Book && newData.get(newItemPosition) is Book) {
var oldBook: Book = oldData.get(oldItemPosition) as Book
var newBook: Book = newData.get(newItemPosition) as Book
return oldBook.name.equals(newBook.name)
}
if (oldData.get(oldItemPosition) is Cat && newData.get(newItemPosition) is Cat) {
var oldCat: Cat = oldData.get(oldItemPosition) as Cat
var newCat: Cat = newData.get(newItemPosition) as Cat
return oldCat.name.equals(newCat.name)
}
return false
}
我的列表里有2种 itemtype ,之前只判断了 itemtype 是不是一样,结果频繁报 IndexOutOfBoundsException 这个异常,搞了半天想死的心都有,最后还是再次品鉴了翻 areItemsTheSame 方法的意义才 OK 的,大家这里碰到同样的问题,请不要着急,按我的思路求做,一准没问题