在ItemAnimator上为RecyclerView禁用onChange动画

时间:2016-03-03 08:08:10

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

我正在使用RecyclerView使用SortedList来提取来自SortedListAdapterCallback的数据。我想停用onChange个活动的动画,但要为onInserted / onRemoved / onMoved保留动画。我尝试在setSupportsChangeAnimations(false)使用的DefaultItemAnimator上调用RecyclerView,但仍会显示动画。如果我致电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次,并且每当它反弹和闪烁时它看起来真的很糟糕。

如何在不诉诸编写完全自定义的ItemAnimator的情况下有效完全禁用onChange动画?

3 个答案:

答案 0 :(得分:6)

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

的值

DefaultItemAnimator代码中执行文字搜索,发现既未查询mSupportsChangeAnimations也未查询getSupportsChangeAnimations() - &gt; 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分支,但是,这可以实现所需的行为。

最近很亲切,但希望这有帮助!

答案 1 :(得分:4)

上面的解决方案对我来说对25.3.1的支持库版本不起作用,因为我想禁用所有回收器视图项的动画。我通过覆盖 userViewModel = ViewModelProviders.of(this).get(UserViewModel.class); userViewModel.getUserList().observe(UserListActivity.this, new Observer<List<User>>() { @Override public void onChanged(@Nullable List<User> user) { adapter.addItems(user); } });

解决了这个问题
SimpleItemAnimator

答案 2 :(得分:3)

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

示例(在Kotlin中):

view.my_recycler_view.itemAnimator?.changeDuration = 0