首页 > 解决方案 > Recycler View添加Item Animator没有动画

问题描述

我正在尝试使用自定义项目动画器在单击按钮后为项目视图设置动画。单击添加按钮后,新项目出现,但没有发生所需的动画。它只是突然出现。我已经尝试了一切,请帮助。

viewholder_add_anim

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <scale
        android:duration = "500"
        android:fromXScale="0%"
        android:fromYScale="0%"
        android:pivotX="50%"
        android:pivotY="50%"
        android:toXScale="100%"
        android:toYScale="100%"/>
</set>

CustomItemAnimator

import android.view.animation.AnimationUtils;

import com.example.bookui.R;

import androidx.recyclerview.widget.DefaultItemAnimator;
import androidx.recyclerview.widget.RecyclerView;

public class CustomItemAnimator extends DefaultItemAnimator {
    @Override
    public boolean animateRemove(RecyclerView.ViewHolder holder) {
        return super.animateRemove(holder);
    }

    @Override
    public boolean animateAdd(RecyclerView.ViewHolder holder) {
        holder.itemView.setAnimation(AnimationUtils.loadAnimation(holder.itemView.getContext(), 
                R.anim.viewholder_add_anim));
        return super.animateAdd(holder);
    }
}

主活动 公共类 MainActivity 扩展 AppCompatActivity {

    private RecyclerView recyclerViewBooks;
    private BookAdapter bookAdapter;
    private List<Book> mdata;
    private Button addButton;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initViews();
        initmdataBooks();
        setUpBookAdapter();
    }

    private void initViews() {
        addButton = findViewById(R.id.add_btn);
        recyclerViewBooks = findViewById(R.id.recyclerView);
        recyclerViewBooks.setHasFixedSize(true);

        recyclerViewBooks.setItemAnimator(new CustomItemAnimator());

        addButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                addBook();
            }
        });
    }

    private void initmdataBooks() {
        mdata = new ArrayList<>();
        mdata.add(new Book(R.drawable.book1));
        mdata.add(new Book(R.drawable.book2));
        mdata.add(new Book(R.drawable.book3));
        mdata.add(new Book(R.drawable.book2));
        mdata.add(new Book(R.drawable.book4));
        mdata.add(new Book(R.drawable.book5));
        mdata.add(new Book(R.drawable.book1));
        mdata.add(new Book(R.drawable.book1));
        mdata.add(new Book(R.drawable.book5));
    }

    private void setUpBookAdapter() {
        bookAdapter = new BookAdapter(mdata);
        recyclerViewBooks.setAdapter(bookAdapter);
    }

    private void addBook() {
        Book book = new Book(R.drawable.book4);
        mdata.add(1, book);
        bookAdapter.notifyDataSetChanged();
    }
}

标签: androidandroid-studioanimationandroid-recyclerviewandroid-animation

解决方案


您必须正确履行ItemAnimator合同而不是盲目地覆盖animateAdd

  1. 不要打电话给super.animateAdd(holder)- 你只会与默认的淡入淡出动画发生冲突。
  2. 您必须跟踪此动画并调用dispatchAddStarting(holder)/dispatchAddFinished(holder)通知当前动画状态的超级实现。
  3. 您必须覆盖更多必须知道您的动画是否正在运行并提供取消选项的方法(在下面的代码中)

覆盖样本DefaultItemAnimator以在新添加的项目上运行放大动画:

public class AddRecAnimator extends DefaultItemAnimator {
    private final static String TAG = "AddRecAnimator";

    // must keep track of all pending/ongoing animations.
    private final ArrayList<AddHolder> pending = new ArrayList<>();
    private final HashMap<RecyclerView.ViewHolder, AddHolder> additions = new HashMap<>();

    @Override
    public boolean animateAdd(RecyclerView.ViewHolder holder) {
        pending.add(new AddHolder(holder));
        return true;    // return true to receive call to runPendingAnimations
    }

    @Override
    public void runPendingAnimations() {
        for (AddHolder ah : pending) {
            ah.start();
        }
        pending.clear();
        super.runPendingAnimations();
    }

    @Override
    public void endAnimation(RecyclerView.ViewHolder item) {
        AddHolder ah = additions.get(item);
        if (ah != null) {
            ah.endAnimation();
        }
        super.endAnimation(item);
    }

    @Override
    public void endAnimations() {
        for (AddHolder ah : pending) {
            ah.resetViewHolderState();
            dispatchAddFinished(ah.holder);
        }
        for (AddHolder ah : additions.values()) {
            ah.resetViewHolderState();
            dispatchAddFinished(ah.holder);
        }
        pending.clear();
        additions.clear();
        super.endAnimations();
    }

    @Override
    public boolean isRunning() {
        return super.isRunning() &&
                !pending.isEmpty() &&
                !additions.isEmpty();
    }

    /**
     * This is container for addition animation. It's also end listener for it.
     */
    private final class AddHolder implements Animation.AnimationListener {
        private final RecyclerView.ViewHolder holder;

        private AddHolder(RecyclerView.ViewHolder holder) {
            this.holder = holder;
            Animation anim = new ScaleAnimation(0.0f, 1.0f, 0.0f, 1.0f,
                    ScaleAnimation.RELATIVE_TO_SELF, 0.5f,
                    ScaleAnimation.RELATIVE_TO_SELF, 0.5f);
            anim.setDuration(500);
            anim.setAnimationListener(this);
            holder.itemView.setAnimation(anim);
            dispatchAddStarting(holder);
        }

        void start() {
            View itemView = holder.itemView;
            Animation a = itemView.getAnimation();
            if (a != null) {
                a.start();
                additions.put(holder, this);
            } else {
                endAnimation(); // invalid state, animation missing
            }
        }

        private void resetViewHolderState() {
            // reset state as if no animation was ran
            Animation a = holder.itemView.getAnimation();
            if (a != null) {
                a.setAnimationListener(null);
                a.cancel();
                holder.itemView.clearAnimation();
            }
            holder.itemView.setScaleX(1f);
            holder.itemView.setScaleY(1f);
        }

        // called when animation ends or is manually cancelled
        protected void endAnimation(){
            additions.remove(holder);
            resetViewHolderState();
            dispatchAddFinished(holder);
            // if all animations in animator are done dispatch they're finished
            if (!isRunning()) dispatchAnimationsFinished();
        }

        @Override
        public void onAnimationStart(Animation animation) {

        }

        @Override
        public void onAnimationEnd(Animation animation) {
            endAnimation();
        }

        @Override
        public void onAnimationRepeat(Animation animation) {

        }
    }
}

推荐阅读