为啥 DefaultItemAnimator 强制更改的 ViewHolders 重新创建?

Posted

技术标签:

【中文标题】为啥 DefaultItemAnimator 强制更改的 ViewHolders 重新创建?【英文标题】:Why is DefaultItemAnimator forcing changed ViewHolders to recreate?为什么 DefaultItemAnimator 强制更改的 ViewHolders 重新创建? 【发布时间】:2021-05-29 13:28:42 【问题描述】:

嘿,所以基本上我最近遇到了RecyclerView 的问题(这里描述了我遇到的问题:RecyclerView recreating ViewHolder instead of rebinding)。我发现,RecyclerView 中的 ItemAnimator 决定了更改后的 ViewHolder 是否只需重新绑定或创建新的 ViewHolder 并进行淡入淡出。

这篇文章解释了一些事情: https://medium.com/android-news/anatomy-of-recyclerview-part-1-a-search-for-a-viewholder-continued-d81c631a2b91

我假设这是 DefaultItemAnimator 的一部分,它决定应该发生什么:

     /**
     * @inheritDoc
     * <p>
     * If the payload list is not empty, DefaultItemAnimator returns <code>true</code>.
     * When this is the case:
     * <ul>
     * <li>If you override @link #animateChange(RecyclerView.ViewHolder, RecyclerView.ViewHolder, int, int, int, int), both
     * ViewHolder arguments will be the same instance.
     * </li>
     * <li>
     * If you are not overriding @link #animateChange(RecyclerView.ViewHolder, RecyclerView.ViewHolder, int, int, int, int),
     * then DefaultItemAnimator will call @link #animateMove(RecyclerView.ViewHolder, int, int, int, int) and
     * run a move animation instead.
     * </li>
     * </ul>
     */
    @Override
    public boolean canReuseUpdatedViewHolder(@NonNull RecyclerView.ViewHolder viewHolder,
            @NonNull List<Object> payloads) 
        return !payloads.isEmpty() || super.canReuseUpdatedViewHolder(viewHolder, payloads);
    

现在我的问题的解决方案是使用一些任意有效负载调用onItemRangeChanged(),它总是会重用ViewHolder

我的问题: 现在我想知道为什么以及何时重新创建整个ViewHolder 并淡入淡出它比仅仅更新它的特定部分更好?在上面提到的交叉引用问题中,我只是更改了 ViewHolder 的一小部分的可见性,但整个事情都被重新创建了,我认为这在性能方面会更差吗?

【问题讨论】:

【参考方案1】:

记住RecyclerView 可以有多个项目视图类型。当notifyItemChanged 导致视图类型发生变化时,无论有效负载如何,视图持有者都没有资格重复使用。

通过替换 viewholders 动画师在这种情况下保持统一的行为。

此外,通过更新 ViewHolder,它们将不会使用适配器 onViewAttachedToWindow(holder),这是人们在布置新项目时可能会期望的,也不会使用 onViewRecycled(holder)onViewDetachedFromWindow(holder),它们会为正在删除的项目调用。

附带说明,如果您在后续范围更新(无负载)期间遇到 onCreateViewHolder 调用,您可能应该增加 RecyclerView.RecycledViewPool 的大小。

【讨论】:

嘿,非常感谢您的回答。好的,如果 RecyclerView 需要这样做以保持一致性,我很好,但有两个问题:如果 ViewHolder 被完全替换,则会有轻微的闪烁(请参阅引用问题中的视频)以及我应该如何动画更改视图内部的状态? ViewHolder 完全重新创建,所有旧的 View 状态都丢失了? @MaxGierlachowski 如果您正在为更改设置动画,请提供更改有效负载并覆盖 onBindViewHolder(holder, position, payloads) 以使用它。 Recyclerview 假设没有有效负载的更改意味着项目被另一个项目完全替换,因此它只是交换视图而不是尝试更改它。

以上是关于为啥 DefaultItemAnimator 强制更改的 ViewHolders 重新创建?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我必须按字段强制这种数据类型,而不是一次强制?

为啥元素搜索反复出现强制转换错误?

为啥不能在 GHC 中强制超功能?

为啥我不能强制解开我的 $string 以用作 TextField 值?

为啥双重否定强制值成为布尔值?

为啥强制转换和转换操作在语法上无法区分?