RecyclerView自定义动画学习记录

这篇博客详细记录了作者在学习RecyclerView自定义动画时的心得,包括添加和移除item时的动画实现。博客指出,需要了解RecyclerView的基础使用才能理解自定义动画。文章通过代码示例解释了如何实现动画,并提出了在实现过程中遇到的item移除后不显示的问题,以及解决方案。此外,还提到了RecyclerView的点击事件实现方法,并分享了Demo的下载链接。

这篇博客主要是用来记录博主在学习RecylcerView的自定义动画时的一些心得与遗留的疑惑,如果有写错的地方,请指出~。希望这篇博客能对大家有帮助~
首先:在观看本博客之前,需要掌握对RecyclerView的一些基础的使用,例如RecyclerView的adapter的实现等。本篇博客主要记录对于RecyclerView的自定义动画的实现,因此其他知识点上不会提太多。
话不多说先上效果:
效果图
这个自定义动画中实现了添加item时的的动画与移除item时的动画。本例能通过点击添加信息按钮和删除信息按钮添加与移除item,和通过点击item来语出RecyclerView的item。
先看移除动画的实现,老样子一步一步来,要定制自定义动画,我们需要继承SimpleItemAnimator类
在这个类中,我们需要重写它的八个方法

public class RecyclerviewAnimation extends SimpleItemAnimator{
    @Override
    public boolean animateRemove(RecyclerView.ViewHolder holder) {
        return false;
    }

    @Override
    public boolean animateAdd(RecyclerView.ViewHolder holder) {
        return false;
    }

    @Override
    public boolean animateMove(RecyclerView.ViewHolder holder, int fromX, int fromY, int toX, int toY) {
        return false;
    }

    @Override
    public boolean animateChange(RecyclerView.ViewHolder oldHolder, RecyclerView.ViewHolder newHolder, int fromLeft, 
                                 int fromTop, int toLeft, int toTop) {
        return false;
    }

    @Override
    public void runPendingAnimations() {

    }

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

    }

    @Override
    public void endAnimations() {

    }

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

由于是实现移除item时的动画,我们需要先实现animateRemove(RecyclerView.ViewHolder holder)这个方法,这个方法在我们调用notifyItemRemoved方法时会被系统调用。

    public boolean animateRemove(RecyclerView.ViewHolder holder) {
        View view = holder.itemView;
        view.setAlpha(1);
        MyAdapter.MyHolder myHolder = (MyAdapter.MyHolder) holder;
        removeAnimatorList.add(myHolder);
        return true;
    }

在这个方法中,我们通过传递过来的ViewHolder参数获取此次需要实行动画的View,并将ViewHolder保存在removeAnimationList中。removeAnimatorList是一个保存持有将要执行动画的View的ViewHolder的List【即removeAnimationList中添加的就是将要执行remove动画的View】。由于是一个移除动画,我们需要的动画效果是让该View渐渐消失(即alpha从1到0)。因此在动画开始之前,我们调用View的setAlpha将参数设1。

animateRemove默认返回false,我们将返回值设置为true,表示在执行完animateRemove方法之后,系统会自动帮我们调用 runPendingAnimations()方法,在runPendingAnimations方法中我们进行移除动画的播放与监听。

public void runPendingAnimations() {
 for(int i = 0;i<removeAnimatorList.size();i++){
            final MyAdapter.MyHolder myHolder = removeAnimatorList.get(i);
            removeRunningAnimatorList.add(myHolder);
            View view = myHolder.itemView;
            final ViewPropertyAnimatorCompat animator = ViewCompat.animate(view);
            animator.alpha(0).setDuration(1000).setListener(new ViewPropertyAnimatorListener() {
                @Override
                public void onAnimationStart(View view) {

                }

                @Override
                public void onAnimationEnd(View view) {
                    animator.setListener(null);
                    view.setAlpha(0);
                    removeRunningAnimatorList.remove(myHolder);
                    dispatchRemoveFinished(myHolder);

                    if(!isRunning()){
                        dispatchAnimationsFinished();
                    }
                }

                @Override
                public void onAnimationCancel(View view) {

                }
            }).start();

        }
        removeAnimatorList.clear();
    }

在这个方法中,循环取出removeAnimatorList中的view,并将正在执行动画的View添加到removeRunningAnimatorList中,removeRunningAnimatorList是一个持有保存正在执行动画的View的ViewHolder的List【即removeRunningAnimatorList添加的是正在执行动画的View】,同时为每个view设置动画和监听器。
在监听到该View的动画结束时,先将View置为透明,再将该View的holder从从removeRunningAnimatorList中移除,并通过 dispatchRemoveFinished()方法告知系统remove动画播放已经结束。
isRunning方法是判断该自定义动画是不是全部结束【即没有正在进行remove动画与将要进行remove动画的view】。如果全部结束则通过 dispatchAnimationsFinished()方法告知系统动画全部结束。
在单个item的动画结束之后,系统会调用endAnimation()法,在该方法中,我们需要做的事情和监听动画结束的方法中一样,将该View的holder从removeAnimatorList中移除并且将View设置为透明。

public void endAnimation(RecyclerView.ViewHolder item) {
        View view = item.itemView;
        view.animate().cancel();

      for (int i = 0; i < removeAnimatorList.size(); i++) {
         if (removeAnimatorList.get(i) == item) {
                    removeAnimatorList.remove(item);
                }
            }
            view.setAlpha(0);
            dispatchRemoveFinished(item);


        if (!isRunning()) {
            dispatchAnimationsFinished();
        }

    }

最后在所有的动画结束之后,将所有要执行动画的View都设置为透明,并将其从removeAnimatorList中移除,同时将正在执行动画View的动画取消

    public void endAnimations() {
    for(int i = 0;i<removeAnimatorList.size();i++){
                MyAdapter.MyHolder myHolder = removeAnimatorList.get(i);
                View view = myHolder.itemView;
                view.setAlpha(0);
                removeAnimatorList.remove(myHolder);
                dispatchRemoveFinished(myHolder);
            }

            for(int i = 0;i<removeRunningAnimatorList.size();i++){
                View view = removeRunningAnimatorList.get(i).itemView;
                view.animate().cancel();
            }
    }

isRunning方法也需要我们重写,该方法用来判断动画是否在运行,只要removeRunningAnimatorList与removeAnimatorList其中之一不为空,那么就可以认为动画在运行

    public boolean isRunning() {
        return  !removeAnimatorList.isEmpty()||
                !removeRunningAnimatorList.isEmpty();
    }

然后为RecylcerView设置animator即可

RecyclerviewAnimation animation = new RecyclerviewAnimation();
recyclerView.setItemAnimator(animation);

效果
remove动画
增加动画也与remove动画流程一致,只需要将View的setAlpha倒过来设置即可。这里就不再贴出代码并分析,有兴趣可以在下面的链接中下载Demo。
最后要提的是关于通过点击RecylerView的item对item进行移除。RecyclerView与ListView不同,ListView有系统写好的item点击监听供我们使用,而RecyclerView没有,因此RecyclerView的点击事件需要我们自行实现。
首先,现在RecyclerView的Adapter中设置一个接口与接口变量

    private OnItemClick click;
    interface OnItemClick{
        void OnItemClick(int position,MyHolder holder);
    }

    public void setOnItemClick(OnItemClick click){
        this.click = click;
    }

在本例中RecyclerView的item为一个TextView。因此在为显示item进行点击监听,实际上可以设置为对TextView的点击监听。

    @Override
    public void onBindViewHolder(final MyHolder holder, final int position) {
        holder.getTextView().setText(messageList.get(position));
        if(click != null){
            holder.getTextView().setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    click.OnItemClick(position,holder);
                }
            });
        }
    }

最后我们只需要在Activity中为Adapter注册监听,并且实现该接口即可。

myAdapter.setOnItemClick(this); 
public void OnItemClick(int position, MyAdapter.MyHolder holder) {
        messageList.remove(position);
        myAdapter.notifyItemRemoved(position);
        Toast.makeText(MainActivity.this,"您点击的第"+(position+1)+"条数据被删除",Toast.LENGTH_SHORT).show();
    }

PS:本Demo存在的问题
在多次调用notifyItemRemoved方法以后会出现个别item不显示的问题
item显示不全
但是在notifyItemRemoveedf方法法之后调用notifyDataSetChanged可以解决item不显示问题,但是删除不会再出现动画效果。
这个问题困扰了我很久,也搜了一些资料,有的说调用notifyItemRangeChanged方法,尝试过这个方法但是被包括在改变范围里的item都会出现移除动画的效果。如果有大神知道如何解决,希望在评论里解答一下这个疑问。


最后本例Demo的下载地址
Demo

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值