Recyclerview DiffUtil 项目更新
Posted
技术标签:
【中文标题】Recyclerview DiffUtil 项目更新【英文标题】:Recyclerview DiffUtil Item Update 【发布时间】:2020-06-04 09:06:56 【问题描述】:我的 recyclerview 中有无限滚动,所以当有新数据时它会更新。我正在使用 DiffUtil 更新 recyclerview 中的数据。 DiffUtil 确实会更新数据,但只要有更新数据,recyclerview 就会滚动到顶部,看起来就像“使用 notifydatasetchanged()”。这是我的 DiffUtil 和用于更新数据的适配器。
class ProductDiffUtil(
val oldProductList: List<ProductModel>, val newProductList: List<ProductModel>
) : DiffUtil.Callback()
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean
return oldProductList[oldItemPosition].id == newProductList[newItemPosition].id
override fun getOldListSize(): Int = oldProductList.size
override fun getNewListSize(): Int = newProductList.size
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean
return oldProductList[oldItemPosition] == newProductList[newItemPosition]
override fun getChangePayload(oldItemPosition: Int, newItemPosition: Int): Any?
return super.getChangePayload(oldItemPosition, newItemPosition)
这是我更新数据的适配器
fun addProductList(productList: List<ProductModel>?)
val diffResult = DiffUtil.calculateDiff(ProductDiffUtil(this.productList, productList!!))
this.productList.addAll(productList)
diffResult.dispatchUpdatesTo(this)
请帮我解决这个问题。当我使用 notifyItemRangeChanged() 时它工作正常......所以我应该使用什么来更新 recyclerview 中的数据以获得最佳实践。
https://drive.google.com/open?id=1SapXW2wusmTpyGCRA9fa0aSLCYNL1fzN
【问题讨论】:
使用 notifyitemrangeinserted 方法。 我最初使用的是 notifyItemRangeInserted() 但 diffUtil 使列表更快,所以我正在尝试使用它。 用DiffUtil.ItemCallback<T>()
代替DiffUtil.Callback
怎么样。
你是如何实现适配器的?你是使用分页库还是定制的?
【参考方案1】:
您只是将以前的内容与新项目进行比较,而不是与添加了所有项目的列表进行比较。
假设this.productList
当前是1,2,3
,而新的productList
是4,5,6
。当你运行时
DiffUtil.calculateDiff(ProductDiffUtil(this.productList, productList!!)
它会将1
与4
、2
与5
等进行比较,并得出结论:一切都发生了变化,并且没有添加新项目。 (注意:这是对 DiffUtil 算法的过度简化,但用于说明这一点)
如果你想使用 DiffUtil:
val oldList = ArrayList(productList)
this.productList.addAll(productList)
val diffResult = DiffUtil.calculateDiff(ProductDiffUtil(oldList, productList!!)
diffResult.dispatchUpdatesTo(this)
或者,由于您确切知道添加了多少项目以及在何处添加,只需使用 notifyItemRangeInserted
并避免复制:
val oldSize = this.productList.size
this.productList.addAll(productList)
notifyItemRangeInserted(oldSize, productList.size)
【讨论】:
fun addProductList(productList: List考虑创建一个通用的 diffUtil 类,而不是为每个适配器创建它。
fun <T>diffList(oldList: List<T>, newList: List<T>, sameItem: (a: T, b: T) -> Boolean): DiffUtil.DiffResult
val callback: DiffUtil.Callback = object : DiffUtil.Callback()
override fun getOldListSize(): Int
return oldList.size
override fun getNewListSize(): Int
return newList.size
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean
return sameItem(oldList[oldItemPosition], newList[newItemPosition])
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean
return oldList[oldItemPosition] == newList[newItemPosition]
return DiffUtil.calculateDiff(callback)
您可以像这样在适配器中使用它:
fun setItems(products: List<Product>)
val oldList = productList
productList = products
diffList(oldList, products, sameItem = a, b -> a.id == b.id ).dispatchUpdatesTo(this)
【讨论】:
【参考方案3】:检查布局管理器是否已经设置并获取当前滚动位置。像这样:
var itemPostion= 0
if(myRecyclerView.layoutmanager != null)
itemPostion = (myRecyclerView.layoutmanager as LinearLayoutManager)
.findFirstCompletelyVisibleItemPosition()
您可以在 GitHub 上查看此示例项目
【讨论】:
这不应该是必要的,他们不应该更换适配器。 @RyanMentley 它没有更换适配器。它恢复了他想要解决的上一个滚动位置,而不是在数据更新时滚动回顶部。 RecyclerView 在更新时默认不会滚动到顶部。这不应该是必要的。以上是关于Recyclerview DiffUtil 项目更新的主要内容,如果未能解决你的问题,请参考以下文章
Android开发Diffutils打造不一样的recyclerview
Android 高性能列表:RecyclerView + DiffUtil