禁用RecyclerView的ItemAnimator中的onChange动画

15
我正在使用一个由SortedListAdapterCallback提供数据的RecyclerView。 我想禁用onChange事件的动画效果,但保留onInserted/onRemoved/onMoved 的动画效果。我试过在 RecyclerView 中使用的 DefaultItemAnimator 上调用 setSupportsChangeAnimations(false) 方法,但仍然有动画效果。如果我调用 setItemAnimator(null) ,所有的动画都会被移除。

我尝试查看了实现,并发现如果 supportsChangeAnimationstrue,则RecyclerView将通过保留旧的 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?
3个回答

15

我在这件事上有些晚了,但是使用 androidx.recyclerview:recyclerview:1.1.0 ,我可以将默认动画器的 changeDuration 设置为 0,从而有效地禁用动画,同时仍然可以正常运行添加/移动/删除动画。无需自定义覆盖 DefaultItemAnimator

示例(使用 Kotlin):

view.my_recycler_view.itemAnimator?.changeDuration = 0

14

浏览代码(我使用的是支持库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分支,当没有要运行的动画时,但是,这样可以实现所期望的行为。

有点晚了,但希望能对您有所帮助!


8

对我来说,上述解决方案在支持库版本为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;
    }
}

2
好的,这可能有效,但问题严格关注禁用onChange并保留其他动画。 - cjurjiu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接