原文 http://www.birbit.com/recyclerview-animations-part-1-how-animations-work/
Listview 是Android最受欢迎的控件之一,虽然它有许多特性,但是它也是相当复杂并且很难修改.
在Lollipop中,Android发布了一个新的控件--RecyclerView,它的插件化结构使得展现collection views更加简单,仅仅通过实现一些简单的contract就可以实现很多不同的功能:
1.how item are laid out
2.item animator
3.item decorations
4.recycling strategy
Predictive Animation
在这篇文章中,我想去深入剖析RecyclerView的内部实现原理,尤其是关于动画是如何实现的.
在Honeycomb中,Android 引入了布局动画LayoutTransition,来实现当ViewGroup布局变化时的过渡动画.这个框架会拿到ViewGroup布局变化前后的状态,然后在两种状态间创建动画进行改变.
但是,列表控件与普通ViewGroup有很大的区别,列表控件中的item与ViewGroup中的子view也有很大的区别,所以我们不能直接使用LayoutTransition.在普通ViewGroup中,如果View是被新加入到ViewGroup中的,它是被当做一个新的View对待的,并且可以使用fade in等动画.但是对于列表,例如,一个item的view变成可见的,可能是因为它前面的item从Adapter中被移除了.在这种情况下,如果使用fade in动画,就会让用户产生改item是被新插入的错觉,但是事实上这个item已经在列表中了,它应该是滚入屏幕的.RecyclerView知道这个item是否是新的,但是却不知道当这个item它原来的位置在哪.同样的,对于滚出屏幕的item(前提没有被adapter移除),RecyclerView同样不知道这个view要被放置在哪.
LayoutTransition failure for a list
RecyclerView如果通过LayoutManger拿到新View的的previous位置,那么LayoutManager不仅需要做一些记录的工作还要多出一些计算的工作.
那么RecyclerView是如何处理这种item的出现和消失动画的呢(指的是那些滚出/滚入屏幕的item)?没错,就是依赖LayoutManager,
LayoutManager通过处理预布局(predictive layout logic)的逻辑,来向RecyclerView提供变化前后item的位置.当Adapter发生改变时,RecyclerView使用了两次Layout处理:
1.第一次Layout(preLayout),RecyclerView会向LayoutManager提供一些信息,让LayoutManager对改变前的状态进行一次layout.
以上面的动图为例,LayoutManager会收到C将要被移除的信息,然后进行layout(将C留下的位置补上),整个环节最重要的部分就
是RecyclerView假装C仍在Adapter中.比如,当LayoutManager要获取index为2的view时,RecyclerView要返回‘C’.
2.第二次Layout(postLayout),RecyclerView让LayoutManager重新布局items,这次‘C’已经被Adapter移除,getViewForPosition(2)拿
到的是‘D’,getViewForPosition(4)拿到的是‘F’.Keep in mind that the backing item for 'C' was already removed from the Adapter,
but since RecyclerView has the View representation of it, it can behave as if 'C' is still there. In other words, RecyclerView
does the bookkeeping for the LayoutManager.
LayoutManger每次调用onLayoutChildren时,它都会暂时detach掉所有的View然后在进行布局,未发生改变的View之前的measure还是有效的,所以这中relayout是很cheap和simple的.
LinearLayoutManager pre layout result: (pink rectangle marks the area visible to the user)*
LinearLayoutManager post layout
在两次布局之后,RecyclerView就可以知道了View的previous location,然后进行正确的动画.
Predictive animation
你也许会问:LayoutManager没有对‘C’的view进行laid out,为什么C还是可见的?
事实上,'C'在pre_layout中是被LayoutManager lai'd out了,但是在post-layout没有被laid out因为它已经不再Adapter中了.它也确实不再是LayoutManager的child,但是它却仍旧是RecyclerView的child,所以此时ItemAnimator可以正常的执行.
Disappearing Items
但是现在还有一个问题就是,将要消失的item.考虑下面这个例子,有一个item被加入到列表中,会将另一个item退出屏幕外.下面是用LayoutTransition实现的效果:
Add Animation Failure
当‘X’被添加到‘A’的后面,F会被挤出屏幕外.LayoutTransition认为‘F’已经被移除,然后对F使用了Fade out 动画.但事实上F仍在列表中.
为了解决这个问题,RecyclerView给LayoutManger提供了API来获取这个信息.在第二次Layout的最后(postLayout),LayoutManager可以调用getScrapList()方法获取那些不会被LayoutManager布局但是却仍旧在Adapter中的Views.然后LayoutManager会假设RecyclerView大到可以展示这些View,对这些View进行lay out.
LinearLayoutManager post layout
这有一个很重要的细节就是,对于那些在动画结束后不再有用的View,LayoutManger会通过调用addDisappearingView而不是addView来告诉RecyclerView,这个View在动画结束后应该被移除.RecyclerView也会添加这个View到the list of hidden views,当postLayout方法返回时,这个View就会被从LayoutManager的children list中被移除.
也许你会认为,对于LinearLayoutManager来说,完全可以单独计算View原来的位置或者将要被放置的的位置,也不必进行两次Layout操作.但是对于有多个item类型的Adapter,如果多个类型同时发生改变,会产生许多临界情况.此外,对于像StaggeredGridLayout这种复杂的LayoutManager,计算item的位置是很繁琐的.目前的这种方式,可以减轻LayoutManager的负担,仅仅需要一点代价就可以完成动画.
Predictive Add Animation
,