RecyclerView 重新创建 ViewHolder 而不是重新绑定

时间:2021-02-17 19:37:55

标签: android android-recyclerview android-viewholder

基本上我有一个带有聊天消息的 RecyclerView。场景如下:

  1. 我添加了一条消息 -> 导致 RecyclerView 添加新的 ViewHolder
  2. 延迟约 2 秒
  3. 我更改了第一条消息并添加了一条新消息 -> 应该使 RecyclerView 重新绑定第一条消息并为第二条消息创建一个新的 ViewHolder

我的问题出在第 3 步。因为第一个 ViewHolder 被删除并创建一个新的然后绑定。这会产生非常轻微的闪烁,这有点烦人,总而言之,这不应该发生。

我正在使用 DiffUtil 来调度更改,整个设置有点复杂,因此很难描述整个画面,但我将其缩小为:

DiffUtil 产生正确的结果并为第一项调用 onItemRangeChanged,为第二项调用 onItemRangeInserted。无论出于何种原因,第一个 ViewHolder 都会被移除,并创建、添加并绑定一个新的 on。

是否有人经历过类似的行为或知道为什么会出现这种情况?我试图通过 RecyclerView 代码进行调试,但这是一场噩梦,我仍然不明白整个事情。第一个 ViewHolder 无法反弹而必须创建新的 createViewHolder 的原因可能是什么?

如果我能提供更多信息,请索取,我会尽力而为。

可以在此处找到显示问题的小型存储库:https://github.com/MaxGierlachowski/recyclerview_viewholder_bug

如果您查看错误日志并查看第 4 个“MESSAGE”日志,您会看到 onChanged 和 onInserted 被分派到适配器,但为 onChanged 创建了一个新的 ViewHolder。我知道 RecyclerView 会检查很多事情,比如动画完成等,但我真的很想知道为什么 ViewHolder 不是反弹而是重新创建。

编辑: 我发现,如果 RecyclerViewmChangedScrap 调用,则应重用的 ViewHolder 位于 mState.isPreLayout() 列表中,而 tryGetViewHolderForPositionByDeadline() 为假,因此函数 {{ 1}} 不搜索 mChangedScrap 列表并创建一个新列表。这仍然不应该发生,ViewHolder 甚至没有动画或其他东西,似乎所有动画都在那时完成了。

闪烁的小视频: flickering effect

2 个答案:

答案 0 :(得分:1)

可能是您的 DiffUtil.Callback 有问题。从 documentation 中,有一个方法 areItemsTheSame(T oldItem, T newItem) 基于结果 diff util 可以删除和添加新的持有者或只是更新现有持有者的内容。确保您的实施正确无误。

areItemsTheSame -> 例如检查左右持有人是相同类型的持有人。 areContentsTheSame -> 在这里您应该检查是否相等。 object1.equals(object2)

答案 1 :(得分:0)

嘿,所以我自己想通了,但我仍然不完全理解为什么这是必要的。因此,ItemAnimator 中的 RecyclerView 决定了更改的 ViewHolder 是被反弹还是重新创建。它将根据下面文章中提到的三个因素来决定它。所以基本上是因为我使用了 DiffUtil,所以我只需要为 onItemRangeChanged 调用提供一些任意值,它会重用 ViewHolder 而不是创建一个新的。

正是这篇文章帮助我理解了这个问题: https://medium.com/android-news/anatomy-of-recyclerview-part-1-a-search-for-a-viewholder-continued-d81c631a2b91