DiffUtil ItemCallback areContentsTheSame() 在更新 ListAdapter 上的项目后始终返回 true [英] DiffUtil ItemCallback areContentsTheSame() always returns true after updating an item on the ListAdapter

查看:30
本文介绍了DiffUtil ItemCallback areContentsTheSame() 在更新 ListAdapter 上的项目后始终返回 true的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在使用 ListAdapter 时,我注意到在更新项目后,DiffUtil.ItemCallback areContentsTheSame() 方法总是返回 true.调试代码我意识到旧的和新的项目是完全一样的(旧的状态消失了).因此,ListAdapter onBindViewHolder() 方法没有被调用来更新列表中的相应行(只有项目的顺序用漂亮的动画改变).>

在 Stackoverflow 上检查其他问题似乎是许多开发人员面临的常见问题:

ListAdapter 未更新 reyclerview 中的项目

ListAdapter 在编辑内容时未更新

DiffUtil.Callback 未按预期工作

Recyclerview + Listadapter 中的项目不会在更新时重绘

带有 DiffUtil.ItemCallback 的 ListAdapter 始终认为对象相同

提交时新列表到 RecyclerView ListAdapter diff 检查总是为 areContentsTheSame() 返回 true

<块引用>

然而,以上答案(如果有)都没有提供正确的解决方案.

它对我有用的唯一方法是每次观察者发出新结果时调用 ListAdapter 上的 notifyDataSetChanged().

但是,如果通过强制 RecyclerView 重绘其内容(它拥有的所有项目)而丢弃您可以获得的所有出色性能,那么使用 ListAdapter 和 submitList() 来通知更改的全部意义是什么?到目前为止显示)?

  1. notifyDataSetChanged():

即使它有效,但如果您决定首先使用 ListAdapter,那肯定不是正确的方法.

  1. viewModel.getObjects().observe(this, listOfObjects -> listAdapter.submitList(new ArrayList<>(listOfObjects)));:

没用.

更新列表中的项目后,我希望看到相应行的 UI 上发生的相应更改(不仅仅是排序顺序).

解决方案

我不知道这是否是针对此特定情况的解决方案,但从描述来看,它听起来与我最近经历的一模一样.

我第一次使用新的 Room + LiveData + ViewModel + DiffUtils 集成,在更新列表后更新 RecyclerView 中的项目时遇到了同样的问题.

我遇到的问题是由于我对更新列表和允许 DiffUtils 按其应有的工作的理解.希望以下内容足够清楚:

我在做什么:

  1. 用户使用对话框更新其项目
  2. RecyclerView 中的列表项已更新为新信息
  3. Room 触发了 LiveData 观察者,因为某些东西可能有改变了
  4. RecyclerView DiffUtils 尝试检查旧的适配器列表和新的
  5. 未检测到任何新内容
  6. 不会触发适配器更新其 UI

我的错误是认为问题出在 .5 中,这导致我花了半天时间来回调试问题.最终,我偶然发现了一个 SO 问题(目前无法找到),这使我找到了问题的正确来源.该问题确实位于 .2 - 更新列表中的项目.

这是问题所在,因为我们甚至在 DiffUtils 有机会比较新旧列表更改之前,就已使用新更改更新了我们的 CURRENT 适配器列表.这意味着每次 DiffUtils 总是将列表与已经包含新列表更改的旧列表进行比较.

解决方案?不要更新列表项,因为它是一个对象,并且列表对象保持相同的引用,导致在使用/引用它的任何地方都更新相同的实例.而是克隆项目对象(如在深度克隆中,我知道这可能很烦人),应用用户对该克隆对象所做的更改,然后使用该克隆更新房间数据库中的项目条目.不要用克隆替换适配器列表项,保留原始项,我们只想更新数据库中的信息,而不是列表,因为 DiffUtils 会负责.

我们在这里基本上要做的是为我们的 Room 数据库创建一个更新有效负载,然后触发 LiveData 观察者将旧列表与新列表(包含更新的项目数据)进行比较,从而在两者之间进行预期的更改检测列表.


简而言之;

这样做:

  • 深度克隆适配器列表项
  • 仅使用新信息更新克隆
  • 使用克隆信息更新数据库

不要这样做:

  • 直接更新适配器列表项
  • 使用列表项信息更新数据库

While using a ListAdapter I noticed that after updating an item the DiffUtil.ItemCallback areContentsTheSame() method was always returning true. Debugging the code I realized that the old and the new item were exactly the same (the old state disappeared). Therefore the ListAdapter onBindViewHolder() method wasn't being called to update the respective row on the list (only the order of the items changed with a nice animation).

Checking other questions on Stackoverflow looks like it's a common issue that many developers have faced:

ListAdapter not updating item in reyclerview

ListAdapter not updating when editing content

DiffUtil.Callback not working as expected

Items in Recyclerview + Listadapter won't redraw on update

ListAdapter with DiffUtil.ItemCallback always considers objects the same

When submitting a new list to RecyclerView ListAdapter the diff check always returns true for areContentsTheSame()

However none of the above answers (if any) have provided a correct solution.

The only way it worked for me was by calling notifyDataSetChanged() on the ListAdapter everytime the observer emitted a new result.

But what's the whole point of using a ListAdapter and submitList() to notify changes if all the great performance you could get is thrown away by forcing the RecyclerView to redraw its content (all the items it has showed so far)?

  1. notifyDataSetChanged():

Even though it works, certainly is not the proper approach to go with if you decided to use a ListAdapter in first place.

  1. viewModel.getObjects().observe(this, listOfObjects -> listAdapter.submitList(new ArrayList<>(listOfObjects)));:

Didn't work.

After updating a item in the list I would like to see the respective changes taking place on the UI (not only the sorting order) for the corresponding row as expected.

解决方案

I don't know if this is the solution for this specific case, but by the description it sounds exactly like something I went through very recently.

I am working with a new Room + LiveData + ViewModel + DiffUtils integration for the first time and I was having the same problem with updating the items in a RecyclerView after updating the list.

The problem I was having was due to my understanding of updating a list and allowing DiffUtils to do its job as it should. Hopefully the following will be clear enough:

What I was doing:

  1. User updates its item using a dialog
  2. The list item in the RecyclerView is updated with the new information
  3. Room triggers the LiveData observer because something might have changed
  4. The RecyclerView DiffUtils tries to check any differences between the old adapter list and the new one
  5. Nothing new was detected
  6. The Adapter is not triggered to update its UI

My mistake was thinking that the problem was in .5 which caused me to spend half a day going back and forth debugging the problem. Eventually I stumbled upon a SO question (cannot find it at the moment) which lead me to the correct source of the problem. That problem is really located in .2 - updating the item in the list.

This is the problem because we are updating our CURRENT adapter list with the new changes even before DiffUtils has a chance of comparing the old and new list changes. What this means is every single time the DiffUtils was always comparing the lists with the old list already containing the changes the new list had.

The solution? Do not update the list item since it is an object and list objects keep the same reference resulting in the same instance being updated everywhere that it is being used/referenced. Instead clone the item object (as in deep clone, I know this might be annoying), apply the changes the user made to that cloned object and then use that clone to update the item entry in the Room Database. DO NOT replace the adapter list item with the clone, leave the original one alone, we only want to update the information in the database, not the list since DiffUtils will take care of that.

What we are essentially doing here is creating an update payload for our Room Database, which will then trigger the LiveData observer into comparing the old list with the new one (containing the updated item data) resulting in the expected change detection between both lists.


In short;

Do this:

  • Deep clone adapter list item
  • Update only the clone with the new information
  • Update the database with the clone information

Don't do this:

  • Update the adapter list item directly
  • Update the database with the list item information

这篇关于DiffUtil ItemCallback areContentsTheSame() 在更新 ListAdapter 上的项目后始终返回 true的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆