Android之RecyclerView的动画详解

   RecyclerView是谷歌V7包下新增的控件,用来替代ListView的使用,它有着如下的特点:

  • 你想要控制其显示的方式,请通过布局管理器LayoutManager
  • 你想要控制Item间的间隔(可绘制),请通过ItemDecoration
  • 你想要控制Item增删的动画,请通过ItemAnimator
  • 你想要控制点击、长按事件

   我们可以看到,低耦合是RecyclerView一个重要的特点,我们切换RecyclerView的表现形式,比如水平或者横向滚动、瀑布流模式或者宫格布局等,都只需要关注LayoutManager即可,而每一个Item的间隔符号也是自定义一个ItemDecoration子类即可,就连每一个Item的View的事件,都单独出来了,可以在onInterceptTouchEvent、onTouchEvent和onRequestDisallowInterceptTouchEvent事件中处理拖动、滑动或者点击,当然了,开放出来这么多功能,对应的实现起来也就比原来的ListView复杂了很多。

    

   说的这些都是RecyclerView的用法,关于它的基本用法网上有很多,就不贴出来了,给个官网例子参考一下:

   RecyclerView官方用法举例 (需要哦)


   下面进入重点,RecyclerView的动画。


   我们知道酷炫的ListView往往一般体现在用户体验和炫目的动画中的,而用户体验比如上拉刷新、下拉加载等等,动画呢则是每一个item进入退出的动画,这样在滑动过程中的出现或者消失就不是那么突兀,体验视觉效果也就更加棒了。上面介绍了在RecyclerView的动画主要是在抽象类RecyclerView.ItemAnimator中完成的,所以我们只需要一句话来设置RecyclerView动画:mRecyclerView.setItemAnimator(itemAnimator)就可以了,接下来我们的关注点就放在了如何去实现ItemAnimator上面。


   在官方给出的Class Overview里面这样介绍的:

This class defines the animations that take place on items as changes are made to the adapter. Subclasses of ItemAnimator can be used to implement custom animations for actions on ViewHolder items. The RecyclerView will manage retaining these items while they are being animated, but implementors must call dispatchAnimationFinished(ViewHolder) when a ViewHolder's animation is finished. In other words, there must be a matching dispatchAnimationFinished(ViewHolder) call for each animateAppearance()animateChange() animatePersistence(), and animateDisappearance() call.

By default, RecyclerView uses DefaultItemAnimator.

   翻译过来,大致的意思是,ItemAnimator子类用来管理ViewHolder的动画,它必须调用上面的那些方法来进行管理。在最后,官方还给出了一个他们已经ok了的动画类,我们可以直接拿来使用的:DefaultItemAnimator

   我们来看一下DefaultItemAnimator源码,它是继承于SimpleItemAnimator,而SimpleItemAnimator也是一个抽象类并且继承于ItemAnimator的,所以我们先来看看SimpleItemAnimator。

   SimpleItemAnimator类的Class Overview写的很清楚,它是一个包装类,用来判断当前的ViewHolder到底是执行移动、移除、添加或者改变等行为的,所以大致上我们可以猜测出它只不过是根据当前的ViewHolder状态来判断到底执行那种操作,看源码也就明白了

   在animateDisappearance方法里(当一个ViewHolder从RecyclerView里面消失时调用,和移除这个ViewHolder操作是完全不同的),根据ViewHolder消失前后的状态信息(保存在ItemHolderInfo类中)来判断到底是进行移动操作animateMove还是进行animateRemove移除操作;在animateAppearance方法里(当一个ViewHolder从RecyclerView里面出现时调用),也是根据状态位置等信息判断是新添加还是仅仅只是从一个位置移动到另外一个位置;animatePersistence方法的意思是并没有调用notifyItemChanged(int) 和 notifyDataSetChanged()但布局发生了改变,比如移动等;animateChange方法在我们主动调用RecyclerView.notifyItemChanged时调用。我们可以看到在这些方法里面都只是调用了抽象方法,分配给子类去做,所以我们来看看DefaultItemAnimator子类到底做了那些处理。

   首先看到的就是一大堆集合,这些集合是存在的ViewHolder和ViewHolder信息的,总的来说,就是在实现的抽象方法中根据判断到底应该放入那个集合中,然后在某个方法中统一进行动画。所以那些实现的抽象方法我们就不看了,主要看的就是runPendingAnimations方法,它是在数据全部处理ok了,开始执行动画的时候调用的方法,接下来看看运行流程:

   animateRemoveImpl:首先进行移除动画,也就是将mPendingRemovals集合中的ViewHolder进行动画,然后清空此集合,这里有一点需要注意的就是,在每一个ViewHolder执行完动画之后,都需要将View还原,否则在你滑动列表的时候,会出现某个或某几个Item表现为动画之后的状态,这个原因涉及到RecycleView中item的重用,就不多讲了,记住这点就行了。

   animateMoveImpl:如果mPendingMoves有则进行

   animateChangeImpl:如果mPendingChanges有则进行

   animateAddImpl:如果mPendingAdditions有则进行

   最后调用endAnimations方法结束动画


   大致流程就是上面说的了,其中里面涉及到一些细节的处理,比如监听动画结束后的操作、调用dispatchAnimationFinished处理动画收尾工作等等。现在我们知道了RecycleView动画的流程了,那么我们可否来定义自己的动画呢?答案当然是肯定的,我们可以继承最基本的类RecyclerView.ItemAnimator,这样就需要自己去重写它的所有方法,这样做实在有点得不偿失的,因为已经有了一个SimpleItemAnimator可以给我们来提供基本处理了,那么我们不必去进行基本的判断,所以这一步可以忽略,这样我们只需要继承SimpleItemAnimator就可以了。

   继承SimpleItemAnimator之后,也是重写它的几个方法,添加、删除、移动方法等等,我们可以模仿DefaultItemAnimator类来进行,将DefaultItemAnimator全部复制到我们自定义的类中,这样就走上面我所说的流程了,既然需要定义我们自己的动画,我们所想的应该就是在runPendingAnimations方法中进行修改了,现在我就简单举一个删除的例子:

   删除,也就是修改animateRemoveImpl里面的动画:

        private void animateRemoveImpl(final ViewHolder holder) {
        final View view = holder.itemView;
        final ObjectAnimator visToInvis = ObjectAnimator.ofFloat(view, "rotationY", 0f, 180f);
        visToInvis.setDuration(getRemoveDuration());
        visToInvis.addListener(new AnimatorListenerAdapter() {
            public void onAnimationStart(Animator animation) {
                super.onAnimationStart(animation);
                dispatchRemoveStarting(holder);
            }

            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                visToInvis.removeListener(this);
                ObjectAnimator.ofFloat(view, "rotationY", 180f, 0f).setDuration(0).start();
                dispatchRemoveFinished(holder);
                mRemoveAnimations.remove(holder);
                dispatchFinishedWhenDone();
            }
        });
        visToInvis.start();
        mRemoveAnimations.add(holder);
    }
   这样做了之后,就可以在mAdapter.notifyItemRemoved(index)方法中进行删除时有动画了,这里的动画我们都可以自己定义,完全可以当作对普通的View进行动画设置一般,我这里只写了一个删除动画,我们也可以修改添加时的动画,不过这个动画就可以让大家自己去写了。

   OK,基本流程讲解和使用方法就说到这儿了,有什么错误的地方大家指出来一起研究!

   最后贴几个相关的github例子供大家参考:

   https://github.com/mikepenz/ItemAnimators

   https://github.com/dkmeteor/RecyclerViewAnimator




你可能感兴趣的:(android技术篇)