正确覆盖RecyclerView动画

时间:2018-08-05 17:12:18

标签: android android-recyclerview

我有一个RecycleView,其中显示项目列表。我像这样为RecyclerView指定默认的动画器:

recyclerView.setItemAnimator( new DefaultItemAnimator() );

一切正常,但是我想使用自己的自定义动画来添加/删除/更新列表中的元素。

我定义了一个自定义动画类,如下所示:

    public class MyAnimator extends RecyclerView.ItemAnimator {

    @Override
    public  boolean animateDisappearance(@NonNull RecyclerView.ViewHolder viewHolder, @NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo) {
        return false;
    }

    @Override
    public  boolean animateAppearance(@NonNull RecyclerView.ViewHolder viewHolder, @Nullable ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo) {
        return false;
    }

    @Override
    public  boolean animatePersistence(@NonNull RecyclerView.ViewHolder viewHolder, @NonNull ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo) {
        return false;
    }

    @Override
    public  boolean animateChange(@NonNull RecyclerView.ViewHolder oldHolder, @NonNull RecyclerView.ViewHolder newHolder, @NonNull ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo) {
        return false;
    }

    @Override
    public  void runPendingAnimations() {

    }

    @Override
    public  void endAnimation(RecyclerView.ViewHolder item) {

    }

    @Override
    public  void endAnimations() {

    }

    @Override
    public  boolean isRunning() {
        return false;
    }
}

并以与对DefaultItemAnimator相同的方式进行设置。动画不再播放了,所以我想它起作用了,但是问题是项目有时会相互堆叠,而当我删除所有项目时,仍然剩下一些,所以我想我丢失了一些东西。

据我了解,animateDisappearance是一种从列表中删除该项目时调用的方法。如果我返回false,就应该跳过动画,据我理解,对吗?

我是否在正确的轨道上?当我在github上查找此示例时,结果很少,总的来说,我似乎找不到如何执行此操作的基本代码示例,而我发现的全部都是数千行代码。

如何在不使用任何外部库的情况下简单地用自己的默认覆盖添加/删除动画?谢谢!

编辑:

我能够通过以下方式覆盖默认动画:

        recyclerView.setItemAnimator(new DefaultItemAnimator() {
            @Override
            public boolean animateRemove(RecyclerView.ViewHolder holder) {
                holder.itemView.clearAnimation();
                final RecyclerView.ViewHolder h = holder;
                holder.itemView.animate()
                        .alpha(0)
                        .setInterpolator(new AccelerateInterpolator(2.f))
                        .setDuration(1350)
                        .setListener(new AnimatorListenerAdapter() {
                            @Override
                            public void onAnimationEnd(Animator animation) {
                                dispatchRemoveFinished(h);
                            }
                        })
                        .start();
                //
                return false;
            }
        } );

动画效果很好,但是由于某种原因,似乎立即触发了“ dispatchRemoveFinished”,因此,在移除视图之后,它们会立即执行此操作,而不是在动画之后调整其余元素。有什么办法可以解决这个问题?

1 个答案:

答案 0 :(得分:2)

实施data.table时,您必须遵循一些规则,否则RecyclerView状态会变得混乱:

  1. 所有返回false的空方法也必须至少调用library(data.table) setDT(dat) reward <- dat[!is.na(X.targetResp), .(reward)] reward # reward # 1: 1 # 2: 2 # 3: 5 才能清除动画状态。

  2. 如果这些方法是开始动画的,则应该RecyclerView.ItemAnimator,存储动画请求并返回true,以调用实际上应该开始动画的dispatchAnimationFinished(viewHolder)

  3. 您需要跟踪正在进行的动画才能正确取消它们。您还将收到对已经进行动画处理的项目的请求。

这是一个示例dispatchAnimationStarted(viewHolder),仅对移除和移动进行动画处理。请注意充当动画数据持有者和动画状态侦听器的内部类:

runPendingAnimations()

您还可以重写SimpleItemAnimator类,该类将ItemAnimator方法解析为public class RecAnimator extends RecyclerView.ItemAnimator { private final static String TAG = "RecAnimator"; private final static int ANIMATION_TYPE_DISAPPEAR = 1; private final static int ANIMATION_TYPE_MOVE = 2; // must keep track of all pending/ongoing animations. private final ArrayList<AnimInfo> pending = new ArrayList<>(); private final HashMap<RecyclerView.ViewHolder, AnimInfo> disappearances = new HashMap<>(); private final HashMap<RecyclerView.ViewHolder, AnimInfo> persistences = new HashMap<>(); @Override public boolean animateDisappearance(@NonNull RecyclerView.ViewHolder viewHolder, @NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo) { pending.add(new AnimInfo(viewHolder, ANIMATION_TYPE_DISAPPEAR, 0)); dispatchAnimationStarted(viewHolder); // new pending animation added, return true to indicate we want a call to runPendingAnimations() return true; } @Override public boolean animateAppearance(@NonNull RecyclerView.ViewHolder viewHolder, @Nullable ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo) { dispatchAnimationFinished(viewHolder); return false; } @Override public boolean animatePersistence(@NonNull RecyclerView.ViewHolder viewHolder, @NonNull ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo) { if (preLayoutInfo.top != postLayoutInfo.top) { // required movement int topDiff = preLayoutInfo.top - postLayoutInfo.top; AnimInfo per = persistences.get(viewHolder); if(per != null && per.isRunning) { // there is already an ongoing animation - update it instead per.top = per.holder.itemView.getTranslationY() + topDiff; per.start(); // discard this animatePersistence call dispatchAnimationFinished(viewHolder); return false; } pending.add(new AnimInfo(viewHolder, ANIMATION_TYPE_MOVE, topDiff)); dispatchAnimationStarted(viewHolder); // new pending animation added, return true to indicate we want a call to runPendingAnimations() return true; } dispatchAnimationFinished(viewHolder); return false; } @Override public boolean animateChange(@NonNull RecyclerView.ViewHolder oldHolder, @NonNull RecyclerView.ViewHolder newHolder, @NonNull ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo) { dispatchAnimationFinished(oldHolder); dispatchAnimationFinished(newHolder); return false; } @Override public void runPendingAnimations() { for (AnimInfo ai: pending) { ai.start(); } pending.clear(); } @Override public void endAnimation(RecyclerView.ViewHolder item) { AnimInfo ai = disappearances.get(item); if (ai != null && ai.isRunning) { ai.holder.itemView.animate().cancel(); } ai = persistences.get(item); if (ai != null && ai.isRunning) { ai.holder.itemView.animate().cancel(); } } @Override public void endAnimations() { for (AnimInfo ai: disappearances.values()) if (ai.isRunning) ai.holder.itemView.animate().cancel(); for (AnimInfo ai: persistences.values()) if (ai.isRunning) ai.holder.itemView.animate().cancel(); } @Override public boolean isRunning() { return !pending.isEmpty() && !disappearances.isEmpty() && !persistences.isEmpty(); } /** * This is container for each animation. It's also cancel/end listener for them. * */ private final class AnimInfo implements Animator.AnimatorListener { private final RecyclerView.ViewHolder holder; private final int animationType; private float top; private boolean isRunning = false; private AnimInfo(RecyclerView.ViewHolder holder, int animationType, float top) { this.holder = holder; this.animationType = animationType; this.top = top; } void start(){ View itemView = holder.itemView; itemView.animate().setListener(this); switch (animationType) { case ANIMATION_TYPE_DISAPPEAR: itemView.setPivotY(0f); itemView.animate().scaleX(0f).scaleY(0f).setDuration(getRemoveDuration()); disappearances.put(holder, this); // must keep track of all animations break; case ANIMATION_TYPE_MOVE: itemView.setTranslationY(top); itemView.animate().translationY(0f).setDuration(getMoveDuration()); persistences.put(holder, this); // must keep track of all animations break; } isRunning = true; } private void resetViewHolderState(){ // reset state as if no animation was ran switch (animationType) { case ANIMATION_TYPE_DISAPPEAR: holder.itemView.setScaleX(1f); holder.itemView.setScaleY(1f); break; case ANIMATION_TYPE_MOVE: holder.itemView.setTranslationY(0f); break; } } @Override public void onAnimationEnd(Animator animation) { switch (animationType) { case ANIMATION_TYPE_DISAPPEAR: disappearances.remove(holder); break; case ANIMATION_TYPE_MOVE: persistences.remove(holder); break; } resetViewHolderState(); holder.itemView.animate().setListener(null); // clear listener dispatchAnimationFinished(holder); if (!isRunning()) dispatchAnimationsFinished(); isRunning = false; } @Override public void onAnimationCancel(Animator animation) { // jump to end state switch (animationType) { case ANIMATION_TYPE_DISAPPEAR: holder.itemView.setScaleX(0f); holder.itemView.setScaleY(0f); break; case ANIMATION_TYPE_MOVE: holder.itemView.setTranslationY(0f); break; } } @Override public void onAnimationStart(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } } } animate...等。