RecyclerView重新创建ViewHolder而不是重新绑定 [英] RecyclerView recreating ViewHolder instead of rebinding
问题描述
基本上,我有一个带有聊天消息的 RecyclerView
.情况如下:
- 我添加一条消息->使
RecyclerView
添加新的ViewHolder
- 延迟约2秒
- 我更改了第一条消息并添加了一条新消息->应该使
RecyclerView
重新绑定第一条消息,并为第二条消息创建一个新的ViewHolder
.
我的问题出在第3步.删除第一个 ViewHolder
,然后创建一个新的,然后绑定它.这样会产生非常轻微的闪烁,这有点令人讨厌,并且所有这些都不应该发生.
我正在使用 DiffUtil
分发更改,整个设置涉及更多点,因此很难描述整个图片,但我将其缩小到以下范围:
DiffUtil
产生正确的结果,并为第一项调用 onItemRangeChanged
,为第二项调用 onItemRangeInserted
.无论出于何种原因,然后都将第一个ViewHolder删除,并创建,添加和绑定新的on.
有人经历过类似的行为还是有线索为什么会这样呢?我尝试通过 RecyclerView
代码进行调试,但这是一场噩梦,我仍然不了解整个过程.
ViewHolder
无法反弹并且必须创建一个新的原因可能是什么原因?如果我能提供更多信息,请要求它,我会尽力而为.
可以在此处找到显示问题的小型存储库:
嘿,所以我自己弄清楚了,但是我仍然不完全理解为什么这样做是必要的.因此, RecyclerView
中的 ItemAnimator
决定更改后的ViewHolder是反弹还是重新创建.它将根据以下文章中提到的三个因素来做出决定.基本上,因为我使用的是 DiffUtil
,所以我只需要为 onItemRangeChanged
调用提供任意内容,它将重用 ViewHolder
而不是创建新的.
正是这篇文章帮助我理解了这个问题: https://medium.com/android-news/anatomy-of-recyclerview-part-1-a-search-for-a-viewholder-continued-d81c631a2b91
Basically I have a RecyclerView
with chat messages. The scenario is as follows:
- I add a single message -> Causes
RecyclerView
to add a newViewHolder
- Delay of about 2 seconds
- I change the first message and add a new one -> Should cause
RecyclerView
to rebind the first message and create a newViewHolder
for the second one.
My problem lies in step 3. as the first ViewHolder
is removed and a new one is created and then bound. This produces a very slight flickering which is a little bit annoying and all in all this shouldn't happen.
I am using DiffUtil
to dispatch changes, the whole setup is a little bit more involved so it is hard to describe the whole picture but I narrowed it down to this:
The DiffUtil
produces the correct result and calls onItemRangeChanged
for the first item and onItemRangeInserted
for the second item. For whatever reason the first ViewHolder is then removed and a new on is created, added and then bound.
Did somebody experienced similar behaviour or has a clue why this could be the case? I tried to debug through the RecyclerView
code but it is a nightmare and I still don't understand the whole thing. What could be the reason that the first ViewHolder
cannot be rebound and a new one has to be created?
If I can provide any more information just ask for it and I will try my best.
A small repository showing the problem can be found here: https://github.com/MaxGierlachowski/recyclerview_viewholder_bug
If you look into the error logs and look at the 4th "MESSAGE" log you see that a onChanged and a onInserted are dispatched to the adapter but there is a new ViewHolder created for the onChanged one. I know that RecyclerView checks for a lot of things like animation completion and so on but I really would like to know why the ViewHolder isn't rebound but recreated.
EDIT:
Something I figured out was that if that createViewHolder
is called by the RecyclerView
the ViewHolder that should be reused is inside the mChangedScrap
list and mState.isPreLayout()
is false so the function tryGetViewHolderForPositionByDeadline()
doesn't search the mChangedScrap
list and creates a new one. Still this shouldn't happen, the ViewHolder
isn't even animated or something and it seems like all animations are finished at that point.
Small video of the flickering:
Hey so I figured it out myself but I still don't fully understand why this is necessary. So the ItemAnimator
inside RecyclerView
decides whether a changed ViewHolder will be just rebound or recreated. It will decide it on three factors that are mentioned in the article below. So basically because I was using the DiffUtil
I just had to provide some arbitrary to the onItemRangeChanged
call and it would reuse the ViewHolder
instead of creating a new one.
It was this article that help me understand the problem: https://medium.com/android-news/anatomy-of-recyclerview-part-1-a-search-for-a-viewholder-continued-d81c631a2b91
这篇关于RecyclerView重新创建ViewHolder而不是重新绑定的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!