Android ItemAnimator
01.ItemAnimator基本概念
- 官方有一个默认Item动画类DafaultItemAnimator,其中DefaultItemAnimator继承了SimpleItemAnimator,在继承了RecyclerView.ItemAnimator
- SimpleItemAnimator 它是一个包装类,用来判断当前的ViewHolder到底是执行移动、移除、添加或者改变等行为。
- DefaultItemAnimator 是执行具体动画类,它负责将viewHolder初始化、保存需要执行动画的ViveHolder以及动画信息、执行具体的动画。
- 其中DefaultItemAnimator中animate + 动作 为名的的方法(比如animateAdd())做的事情有:计算动画信息,保存动画信息,初始化view的状态,且可以控制该VIewHolder是否执行该次动画,如果返回值为false那么那么不会执行该ViewHolder的改次动画;
- DefaultItemAnimator中animate + 动作 + Impl 为名的方法,做的动作是执行具体的动画动作。
- runPendingAnimations是最终执行具体动画的方法
02.如何使用ItemAnimator
代码如下所示
1
2//设置默认的动画模式
recyclerView.setItemAnimator(new DefaultItemAnimator());RecyclerView能够通过mRecyclerView.setItemAnimator(ItemAnimator animator)设置添加、删除、移动、改变的动画效果。
03.几个重要方法说明
- SimpleItemAnimator类(继承自ItemAnimator),该类提供了一系列更易懂的API,在自定义Item Animator时只需要继承SimpleItemAnimator即可:
- animateAdd(ViewHolder holder): 当Item添加时被调用。
- animateMove(ViewHolder holder, int fromX, int fromY, int toX, int toY): 当Item移动时被调用。
- animateRemove(ViewHolder holder): 当Item删除时被调用。
- animateChange(ViewHolder oldHolder, ViewHolder newHolder, int fromLeft, int fromTop, int toLeft, int toTop): 当显式调用notifyItemChanged()或notifyDataSetChanged()时被调用。
- 注意几点问题
- 当Xxx动画开始执行前(在runPendingAnimations()中)需要调用dispatchXxxStarting(holder),执行完后需要调用dispatchXxxFinished(holder)。
- 这些方法的内部实际上并不是书写执行动画的代码,而是将需要执行动画的Item全部存入成员变量中,并且返回值为true,然后在runPendingAnimations()中一并执行。
04.插入一条数据的动画
代码如下所示
1
2
3
4
5
6public boolean animateAdd(ViewHolder holder) {
this.resetAnimation(holder);
holder.itemView.setAlpha(0.0F);
this.mPendingAdditions.add(holder);
return true;
}如何执行动画分析
- DefaultItemAnimator实现了SimpleItemAnimator的animateAdd()方法,该方法只是将该item添加到mPendingAdditions中,等到runPendingAnimations()中执行。
接着看runPendingAnimations()的实现
- 该方法是执行remove,move,change,add动画,执行顺序为:remove动画最先执行,随后move和change并行执行,最后是add动画。为了简化,我们将remove,move,change动画执行过程省略,只看执行add动画的过程,如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31public void runPendingAnimations() {
//1、判断是否有动画要执行,即各个动画的成员变量里是否有值。
//2、执行remove动画
//3、执行move动画
//4、执行change动画,与move动画并行执行
//5、执行add动画
if (additionsPending) {
final ArrayList<ViewHolder> additions = new ArrayList<>();
additions.addAll(mPendingAdditions);
mAdditionsList.add(additions);
mPendingAdditions.clear();
Runnable adder = new Runnable() {
@Override
public void run() {
for (ViewHolder holder : additions) {
animateAddImpl(holder); //***** 执行动画的方法 *****
}
additions.clear();
mAdditionsList.remove(additions);
}
};
if (removalsPending || movesPending || changesPending) {
long removeDuration = removalsPending ? getRemoveDuration() : 0;
long moveDuration = movesPending ? getMoveDuration() : 0;
long changeDuration = changesPending ? getChangeDuration() : 0;
long totalDelay = removeDuration + Math.max(moveDuration, changeDuration);
View view = additions.get(0).itemView;
ViewCompat.postOnAnimationDelayed(view, adder, totalDelay); //等remove,move,change动画全部做完后,开始执行add动画
}
}
}
05.局部刷新闪屏问题
- 对于RecyclerView的Item Animator,有一个常见的坑就是“闪屏问题”。
- 这个问题的描述是:当Item视图中有图片和文字,当更新文字并调用notifyItemChanged()时,文字改变的同时图片会闪一下。这个问题的原因是当调用notifyItemChanged()时,会调用DefaultItemAnimator的animateChangeImpl()执行change动画,该动画会使得Item的透明度从0变为1,从而造成闪屏。不过问题不是很严重……