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
    6
    public 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
    31
    public 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,从而造成闪屏。不过问题不是很严重……