【问题标题】:Disable onChange animations on ItemAnimator for RecyclerView为 RecyclerView 禁用 ItemAnimator 上的 onChange 动画
【发布时间】:2016-03-03 08:08:10
【问题描述】:

我正在使用RecyclerView 使用SortedListAdapterCallback 提供来自SortedList 的数据。我想为onChange 事件禁用动画,但为onInserted/onRemoved/onMoved 保留它们。我曾尝试在RecyclerView 使用的DefaultItemAnimator 上调用setSupportsChangeAnimations(false),但动画仍然出现。如果我调用setItemAnimator(null) 所有动画会按预期成功删除。

我尝试查看实现,看起来如果supportsChangeAnimationstrueRecyclerView 将通过保留旧的 viewHolder 并将其交叉淡入淡出到新的 viewHolder 来动画更改事件。我不想那样。如果supportsChangeAnimationsfalse,那么旧的和新的viewHolders 将是同一个对象,取而代之的是从x 到x 的onMoved 动画(即没有实际移动)。然而,这意味着该项目将获得令人讨厌的反弹效果。我也不想要那个,我根本不想要动画。 :(

来自 DefaultItemAnimator.java:

@Override
public boolean animateChange(ViewHolder oldHolder, ViewHolder newHolder,
        int fromX, int fromY, int toX, int toY) {
    if (oldHolder == newHolder) {
        // Don't know how to run change animations when the same view holder is re-used.
        // run a move animation to handle position changes.
        return animateMove(oldHolder, fromX, fromY, toX, toY);
    }
    ...

有时当我加载我的列表时,我会异步获取一些数据并更新项目 1-3 次,每次弹跳和闪烁时看起来真的很糟糕。

我如何有效地完全禁用 onChange 动画而不求助于编写完全自定义的 ItemAnimator?

【问题讨论】:

    标签: android android-recyclerview android-support-library


    【解决方案1】:

    查看代码(我使用的是支持库 25.2.0):setSupportsChangeAnimations(<value>) 是抽象类SimpleItemAnimator 上的一个方法,它也是DefaultItemAnimator 的超类。在内部,它修改了mSupportsChangeAnimations 的值。

    DefaultItemAnimator 的代码中执行文本搜索,显示既没有查询mSupportsChangeAnimations,也没有查询getSupportsChangeAnimations() --> DefaultItemAnimator 实际上忽略了这个标志。

    正确的解决方案是通过以下方式扩展DefaultItemAnimator

    public class CustomItemAnimator extends DefaultItemAnimator {
    @Override
    public boolean animateChange(RecyclerView.ViewHolder oldHolder, RecyclerView.ViewHolder newHolder, int fromX, int fromY, int toX, int toY) {
        if (getSupportsChangeAnimations()) {
            return super.animateChange(oldHolder, newHolder, fromX, fromY, toX, toY);
        } else {
            if (oldHolder == newHolder) {
                if (oldHolder != null) {
                    //if the two holders are equal, call dispatch change only once
                    dispatchChangeFinished(oldHolder, /*ignored*/true);
                }
            } else {
                //else call dispatch change once for every non-null holder
                if (oldHolder != null) {
                    dispatchChangeFinished(oldHolder, true);
                }
                if (newHolder != null) {
                    dispatchChangeFinished(newHolder, false);
                }
            }
            //we don't need a call to requestPendingTransactions after this, return false.
            return false;
        }
    }
    

    请参阅文档 animateChange(...) 以了解为什么在没有运行动画时需要调用 dispatchChangeFinished(...)

    当没有要运行的动画时,可能有一种更优雅的方式来编写 else 分支,但是,唉,这实现了所需的行为。

    有点晚了,但希望这会有所帮助!

    【讨论】:

      【解决方案2】:

      我参加这个聚会有点晚了,但是使用androidx.recyclerview:recyclerview:1.1.0 我可以将默认动画师的changeDuration 更改为0,这可以有效地禁用动画,同时允许添加/移动/删除动画继续正常运行。无需自定义覆盖 DefaultItemAnimator

      示例(在 Kotlin 中):

      view.my_recycler_view.itemAnimator?.changeDuration = 0
      

      【讨论】:

        【解决方案3】:

        上述解决方案不适用于我的支持库版本 25.3.1,因为我想禁用所有回收站视图项目的动画。我通过覆盖SimpleItemAnimator解决了它:

        private class NoAnimationItemAnimator extends SimpleItemAnimator {
            @Override
            public boolean animateRemove(RecyclerView.ViewHolder holder) {
                dispatchRemoveFinished(holder);
        
                return false;
            }
        
            @Override
            public boolean animateAdd(RecyclerView.ViewHolder holder) {
                dispatchAddFinished(holder);
        
                return false;
            }
        
            @Override
            public boolean animateMove(RecyclerView.ViewHolder holder, int fromX, int fromY, int toX, int toY) {
                dispatchMoveFinished(holder);
        
                return false;
            }
        
            @Override
            public boolean animateChange(RecyclerView.ViewHolder oldHolder, RecyclerView.ViewHolder newHolder, int fromX, int fromY, int toX, int toY) {
                dispatchChangeFinished(oldHolder, true);
                dispatchChangeFinished(newHolder, false);
        
                return false;
            }
        
            @Override
            public void runPendingAnimations() {
                // stub
            }
        
            @Override
            public void endAnimation(RecyclerView.ViewHolder item) {
                // stub
            }
        
            @Override
            public void endAnimations() {
                // stub
            }
        
            @Override
            public boolean isRunning() {
                return false;
            }
        }
        

        【讨论】:

        • 好吧,它可能会起作用,但问题与仅禁用 onChange 和保留其他动画有关
        猜你喜欢
        • 1970-01-01
        • 2023-02-17
        • 1970-01-01
        • 2015-07-02
        • 1970-01-01
        • 2014-09-02
        • 2016-01-25
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多