为 RecyclerView 禁用 ItemAnimator 上的 onChange 动画 [英] Disable onChange animations on ItemAnimator for RecyclerView

查看:21
本文介绍了为 RecyclerView 禁用 ItemAnimator 上的 onChange 动画的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am using a RecyclerView fed with data from a SortedList using a SortedListAdapterCallback. I want to disable animations for onChange events, but preserve them for onInserted/onRemoved/onMoved. I have tried calling setSupportsChangeAnimations(false) on the DefaultItemAnimator used by the RecyclerView, but the animation still appears. If I call setItemAnimator(null) all animations are successfully removed as expected though.

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

I tried looking at the implementation and it seems like if supportsChangeAnimations is true, the RecyclerView will animate change events by keeping the old viewHolder and cross-fade it to the new viewHolder. I don't want that. If supportsChangeAnimations is false, the old and new viewHolders will however be the same object, and there will instead be an onMoved animation from x to x (i.e., no actual move). This however means that the item will get an annoying bounce effect. I don't want that either, I want no animation at all. :(

来自 DefaultItemAnimator.java:

From 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 次,而且每次都弹跳和闪烁时看起来真的很糟糕.

Sometimes when I load my list I asynchronously fetch some data and update items 1-3 times, and it looks really crappy when it bounces and flickers every time.

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

How do I effectively completely disable onChange animations without resorting to writing a completely custom ItemAnimator?

推荐答案

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

Looking through the code (I'm using support library 25.2.0): setSupportsChangeAnimations(<value>) is a method on the abstract class SimpleItemAnimator, which is also DefaultItemAnimator's superclass. Internally, it modifies the value of mSupportsChangeAnimations.

DefaultItemAnimator 的代码中执行文本搜索,发现 mSupportsChangeAnimationsgetSupportsChangeAnimations() 都没有被查询 -->code>DefaultItemAnimator 从字面上忽略这个标志.

Performing a text search in DefaultItemAnimator's code, reveals that neither mSupportsChangeAnimations, nor getSupportsChangeAnimations() are queried --> the DefaultItemAnimator literally ignores this flag.

正确的解决方案是按以下方式扩展DefaultItemAnimator:

The correct solution is to extend the DefaultItemAnimator in the following manner:

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(...).

See docs animateChange(...) to understand why it was needed to call dispatchChangeFinished(...) when no animations were run.

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

Probably there's a more elegant way to write the else branch when there are no animations to be run, but alas, this achieves the desired behavior.

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

Kind'of late, but hope this helps!

这篇关于为 RecyclerView 禁用 ItemAnimator 上的 onChange 动画的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆