RecyclerView.ItemAnimator源码解析

RecyclerView动画的核心执行类ItemAnimator,当继承一个ItemAnimator时,有如下几个方法需要被实现:

通过实现以上几个接口方法就能实现不同的动画效果,以上几个方法有4个方法比较重要:

  1. animateDisappearance
    当RecyclerView中的item在屏幕上由可见变为不可见时调用此方法

  2. animateAppearance
    当RecyclerView中的item显示到屏幕上时调用此方法

  3. animateChange
    当RecyclerView中的item状态发生改变时调用此方法(比如notifyItemChanged)

  4. runPendingAnimations
    统筹RecyclerView中所有的动画,统一启动执行

然而以上几个接口方法在RecyclerView执行动画时都是何时被调用的呢?

带着疑问,来看下RecyclerView动画触发流程,以添加一个item为例,当我们在RecyclerView中新加一个item时,调用一下代码:

在自定义Adapter中,先向数据源添加一个model对象,然后调用notifyItemInserted(position)方法,通知适配器和RecyclerView我们新插入了一条数据,接着来看下notifyItemInserted中的代码,如下:

可以看到,在notifyItemInserted方法中,只是调用了一个观察者的通知方法并将position传递给它。而这个mObervable是在RecyclerView.Adapter初始化时一并初始化的AdapterDataObservalbe类型对象,跟进去看下它的notifyItemRangeInserted方法:

for循环遍历了一个mObservers,并调用每一个Observer的onItemRangeInserted方法。

这个mObservers是Observalbe中的一个全局集合变量,当调用RecyclerView.setAdapter(Adapter)时,会调用adapter.RegisterAadpterDataObserver(mObserver)方法给Adapter对象添加一个观察者对象Observer。此Observer是RecyclerViewDataObserver类型对象。

继续跟踪代码,进入RecyclerViewDataObserver的onItemRangeInserted方法,代码如下:

首先,调用一个mAdapterHelper的onItemRangeChaged方法将所要进行的操作(比如ADD)保存在AdapterHelper内部的一个集合中,如果此方法返回true,则继续调用triggerUpdateProcessor方法, 代码go on!

在triggerUpdateProcessor()方法中,调用了ViewCompat.postAnimation方法,发布一个mUpdateChildViewsRunnable执行一段异步代码, 用脚后跟也能想到这是一个Runnable对象,点进去看一下:

发现,最终调用了一个consumePendingUpdateOperations()方法,代码跟的好累啊,宝宝心里苦但是不说T_T 曾经有个骚年说过:只要有恒心 铁棒磨成针!

在consumePendingUpdateOperations中,代码比较多,但是比较重要的是红色方框中的 dispatchLayout(), 由名字也能知道,此方法的目的是重新布局RecyclerView的子控件,代码具体如下:

此方法官方API对它做了如下解释:专门用来处理由于layout布局改变而触发的animation动画, 动画总共有5种类型:

  • PERSISTENT :  items在layout布局前和布局后都是可见状态

  • REMOVED :  items在重新布局前可见,重新布局后不可见

  • ADDED:  items在重新布局前不存在,当重新布局后由APP添加到视图当中

  • DISAPPEARING : items在适配器中的数据源中存在但是在屏幕上是由可见状态变为不可见状态,        一般是一个item被滑动出屏幕时的动画

  • APPEARING:  items跟DISAPPEARING正好相反的状态

diapatchLayout方法分三步进行布局 依次是:

  • dispatchLayoutStep1() 

    记录重新调用onLayout之前,用户操作的View的相关信息(getLeft(), getRight(), getTop(), getBottom()等)

  • dispatchLayoutStep2() 

    进行真正意义上的layout工作,只是调用onLayoutChildren方法递归摆放子控件的位置,此方法可能会被调用多次

  • dispatchLayoutStep3() 

    当dispatchLayoutStep2()进行完layout操作之后,RecyclerView在此方法中记录下重新布局之后,用户所操作View的相关信息(同样是getLeft(), getRight(), getTop(), getBottom()) 

首先来看下dispatchLayoutStep1() :

如红色框中所示mItemAnimator!仿佛终于看到希望了 哈哈。mItemAnimator.recordPreLayoutInfomation方法记录了在重新布局之前所操作的View的信息,并将信息都封装在一个ItemHolderInfo的对象当中,代码比较简单 如下所示:

继续查看obtainHolderInfo()方法:

就是简单的通过new ItemHolderInfo的方式返回了一个ItemHolderInfo对象,获取了这个ItemHolderInfo对象之后,会通过setFrom方法传入一个ViewHolder对象,并将ViewHolder对象中的itemView的getLeft  getTop  getRgith  getBottom依次赋值给ItemHolderInfo的left  top  right  bottom变量。最后这个ItemHolderInfo被添加到一个ViewInfoStore的对象中进行保存。到这里dispatchLayoutStep1该做的事基本已经完成。dispatchLayoutStep2()就是单纯的调用onLayoutChildren方法布局子控件,这里不做过多叙述,直接看dispatchLayoutStep3()方法, 代码如下:

首先还是获取ItemHolderInfo,但是此时的ItemHolderInfo中保存的是在layout重新布局之后View的相关信息,然后将此ItemHolderInfo也保存在ViewInfoStore对象中。

到这步为止,添加一个item的前后的View信息都已经被保存在ViewInfoStore对象当中,有了这两个view信息之后,我们就有了自定义动画的起始值^_^

最后调用ViewInfoStore的process方法真正的执行处理操作,并传入mViewInfoProcessCallback回调接口, 代码如下:

通过判断InfoRecord的flags值,依次调用callback的processXXX方法,callback就是之前传进来的mViewInfoProcessCallback对象。

Finally !革命终于成功。

animateChange、animateDisappearance、animateAppearance、postAnimationRunner中依次调用了ItemAnimator的animateChange、animateDisappearance、animateAppearance、runPendingAnimations方法。

这几个方法就是ItemAnimator暴露的接口方法。在这三个方法中可以通过不同的逻辑实现不同的动画效果。

  • animateChange方法中传入了layout之前的ViewHolder和layout之后的ViewHolder对象,通过这两个ViewHolder对象获取其中的itemView进行动画效果

  • animateAppearance和animateDisappearance中都是传入的layout之后的ViewHolder对象,Android OS官方API给我们的说法是可以在自定义的ItemAnimator中保存两个ViewHolder的集合,当调用animateAppearance和animateDisappearance时,就向集合中添加ViewHolder对象。

  • 最后在调用runPendingAnimations时统一遍历这两个集合中所有的ViewHolder对象,并依次执行动画。

最后说一句

原创不易,如果觉得写得不错就点个"在看"吧,或者转发更佳????????????

也可以加我微信,加群讨论更多技术问题

或者关注公众号,查看更多文章

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值