美文网首页
RecyclerView animations - Androi

RecyclerView animations - Androi

作者: 琼珶和予 | 来源:发表于2018-12-23 23:03 被阅读0次

  最近在看别人关于RecyclerView的博客,恰巧看到一位国外大神的博客,是关于RecyclerView动画的。我从那篇博客里面学习到了很多的东西,觉得有必要把它分享出来,所以我打算将它翻译成中文,不过还是建议大家去看英文原文。
  原文链接:RecyclerView animations - AndroidDevSummit write-up,原文是需要翻墙的哦。


  RecyclerView Animations and Behind the Scenes - 我们再来回到一个演示视频里面来。在所有移动平台上,有一个不争的事实,列表视图(list views)或者更一般的视图集合是最常见的视图模式,所以,尽可能了解他们的原理是非常重要的。
  今天,我们来借助于Android Dev的教学视频,来仔细的了解一下RecyclerViewItemAnimator

1. Default Animations in RecyclerView

  谷歌官方通过默认的几种操作,例如:addingremovingchanging,为我们提供了如下的动画:

  1. 淡入和淡出(对应着adding操作和removing操作)
  2. 位移(把剩余的items移动到正确位置上)
  3. 同时渐变(淡入和淡出同时执行,对应着update特定item的操作)

  官方为我们提供了RecyclerView.ItemAnimator的一个子类--DefaultItemAnimator实现了上面三种动画,同时DefaultItemAnimatorRecyclerView内部作为默认动画在使用。
  如果你对DefaultItemAnimator的原理感兴趣,推荐去看它的源码。源码只有600多行,非常的简洁。
  简而言之,DefaultItemAnimator的原理主要可以分为两步:

  1. 准备每个动画(每个操作可能会同时进行多个动画),然后放入即将执行的动画数组里面。
  2. 执行所有的动画。

  基于下面的Demo,让我们看看delete的动画是怎么执行的。



  (1 ~ 3步会以不同的顺序或者混合执行)

  1. 准备add动画(那些不可见但是即将出现在屏幕中的item,当然是必须保证有足够的空间)。
  2. 准备delete动画(被点击的item)
  3. 准备move动画(应该被移动到正确位置的items)
  4. 执行所有准备好的动画(这里面有很多的逻辑,比如,move动画执行完成之后,才执行位移动画--可以通过ViewCompat.postOnAnimationDelayed()了解得更多)。

2. PredictiveItemAnimations

  从用户体验来看,PredictiveItemAnimations确实比较简单,但是从实现原理来说的话,我们更应该关心在执行add或者remove操作之后,之前不可见但是此时应该显示的items和这些items执行着的动画。
  我们必须明白的是,从技术上来讲,在RecyclerView中,屏幕中可见的Item才算是实时存在的。因此,在这种情况下,也就是我们前面所说的操作(remove 操作),屏幕下方会有很多还没有移动的items。因为在此之前,他们还不存在。在这种情况下,那些在屏幕下方的items出现在屏幕时,会执行appearance的动画,在DefaultItemAnimator中,就是淡入的动画。
  再或者,我们可以知道的是,每个item从哪里来的。这里需要注意的是,RecyclerView.ItemAnimator只负责,每个ItemView从初始状态到最终状态的动画过程,而每个ItemView的如何布局,则依然还是LinearLayoutManager(或者RecyclerView.LayoutManager)。
  LayoutManager有一个public的方法supportsPredictiveItemAnimations(),这个方法默认返回为false。当LayoutManager的条件允许时,这个方法会返回为true。当屏幕下方的items出现在屏幕时,如果supportsPredictiveItemAnimations()返回为true,此时这些Item执行是位移动画,而不是appearance动画。
  在我们的代码中,我们是通过如下方式来设置是否开启的。

//MainActivity.java
LinearLayoutManager layoutManager = new LinearLayoutManager(this) {
    @Override
    public boolean supportsPredictiveItemAnimations() {
        return cbPredictive.isChecked();
    }
};
rvColors.setLayoutManager(layoutManager);

  效果如下(仔细看,最后一个item是通过位移动画出现在屏幕当中的)


  如果你对supportsPredictiveItemAnimations()方法感兴趣的话,或者你正在设计自己的LayoutManager并且想要知道supportsPredictiveItemAnimations()方法相关细节,最好的方式就是看官方文档:supportsPredictiveItemAnimations()

3. Custom change Item animations

  在DefaultItemAnimator中, change动画是执行cross-fade(同时渐变)动画。基于一个例子,我们实现了一个同时执行很多动画的change动画。


  通过上面的例子,我们可以知道,我们的item将两个itemView的text拧在了一起,并且background的颜色也从一个颜色渐变成了另一个颜色。我们是通过继承DefaultItemAnimator来实现的。

4. Items animation under the hood

  change动画是展现ItemAnimator工作原理的最简单的方式。值得说一句的是,这里这个change动画实际上跟add动画或者remove动画没有很大的区别。
  这里有最终的实现--MyItemAnimator

(1). Notify change

  现在,让我们从头开始来看。我们的list views被展示出来,同时RecyclerView使用自定义的动画--MyItemAnimator为了每个itemView做动画。


  当我们点击一个itemView时,下面的代码会被执行:
//ColorsAdapter.java
public void changeItemAtPosition(int position) {
    colors.set(position, ColorsHelper.getRandomColor());
    notifyItemChanged(position);
}

  从notifyItemChanged方法开始(直接调用notifyItemChanged方法,同时给出点击的position和被点击的item数量。),我们就会通知RecyclerView--item应该被更新了。

(2). Record recent state

  现在,Animators通过调用 recordPreLayoutInformation(RecyclerView.State state, RecyclerView.ViewHolder viewHolder, int changeFlags, List payloads)(通过文档可以了解得更多)记录每个ItemView最近的状态值了。在这个方法里面,我们会访问被更新的View的ViewHolder,同时还会保存一些状态值(尤其是做动画的那些属性)。
  状态值保存在一个返回对象ItemHolderInfo里面的。
  这是我们的实现:

private class ColorTextInfo extends ItemHolderInfo {
    int color;
    String text;
    
    //...
    
}

  默认的ItemHolderInfo保存着ItemView的边界信息,我们在里面额外的加了做动画的background color 和text。
recordPreLayoutInformation()方法会收集显示的Views的信息,虽然有些ItemView没有被更新(在这种情况,ItemViewchangeFlags被设置为ViewHolder.FLAG_BOUND,等于0)。其它的flags表示正在请求某种操作(change动画对应着值为2 - ViewHolder.FLAG_UPDATE)。
  此时效果如下(我怀疑大佬将gif上传成png了):

(3). Bind new view

  此时,我们的Adapter正在请求bind一个新的ViewHolder,作为ItemView最新的数据。

@Override
public void onBindViewHolder(ColorViewHolder holder, int position) {
    int color = colors.get(position);
    holder.itemView.setBackgroundColor(color);
    holder.tvColor.setText("#" + Integer.toHexString(color));
}

  这就意味着,我们的ItemView将会被改变,效果如下:


(4). Record new state

  现在我们回到ItemAnimator。此时就会调用recordPostLayoutInformation(RecyclerView.State state, RecyclerView.ViewHolder viewHolder)方法来记录ItemView最终的状态值(通过文档可以了解得更多)。此时,我们会再一次的访问ItemView的ViewHolder,但是此时的ItemView是新的ItemView,将重要的信息保存在ItemHolderInfo(我们的ColorTextInfo)。

(5). Play (or just prepare animation)

  RecyclerView.ItemAnimator有一系列的方法用于不同的动画操作,前面我们说的animateChange方法就会被调用,同时这里也是准备动画绝佳时机。我们有如下4个参数来帮助我们完成动画的执行(或者准备):

  • RecyclerView.ViewHolder oldHolder
  • RecyclerView.ViewHolder newHolder
  • ItemHolderInfo preInfo
  • ItemHolderInfo postInfo
      oldHoldernewHolder分别表示itemView布局前和布局后。在我们的case下,这俩是同一个对象,因为如下代码:
@Override
public boolean canReuseUpdatedViewHolder(RecyclerView.ViewHolder viewHolder) {
    return true;
}

  这就意味着,对于动画,我们没有必要将ViewHolder分离(因为,我们执行动画是基于ItemHolderInfo
  preInfopostInfo 对象 分别来自于recordPreLayoutInformation()方法和recordPostLayoutInformation()方法。
  正如你所知,我们的View已经bind了最终的状态,此时我们可以调用animateChange方法,ItemView就从初始状态(保存在preInfo)开始,准备动画和可选的执行动画到最终状态。
  为什么强调可选呢?
  animateChange()方法返回的是一个布尔值,其中false表示动画已经被执行了,true表示动画正在准备,保存和等待执行。
  在false的情形下,我们需要记得在执行最后调用dispatchAnimationFinished方法来告诉RecyclerView动画已经结束了。代码如下:

overallAnim.addListener(new AnimatorListenerAdapter() {
    @Override
    public void onAnimationEnd(Animator animation) {
        dispatchAnimationFinished(newHolder);
    }
});

  这就告诉了RecyclerView,此时这个ViewHolder可以被复用了。
  其他情况就是animateChange()方法返回为true。

(6). Play all pending animations

  这次更新,我们同时会执行多个动画(在add或者remove操作中,有位移动画、出现的动画或者消失的动画),在执行animate...()方法时,会通过某一个方式来保存所有即将执行的动画(在DefaultItemAnimator中,使用的ArrayList),然后会调用runPendingAnimations方法来执行所有准备好的动画。
  这里还有其他的操作没有介绍(比如,cancel动画),这里我留给大家,让大家自己去探索。
  整个执行流程以简短的方式来展示,效果如下:

5. Example app and source code

  Chet Haase 和 Yigit Boyar 的视频有实现的代码: RecyclerView Animations and Behind the Scenes,这些不是谷歌官方的,只是比较相似而已。
 本文没有介绍重复点击一个ItemView,会cancel之前的动画,然后执行新的动画这种情况,但是在视频里面有介绍。

6. Source code

  本文完整的代码已经上传到github:https://github.com/frogermcs/RecyclerViewAnimations

7. Author

  Miroslaw Stanek
  移动开发主管:Azimo Money Transfer

相关文章

网友评论

      本文标题:RecyclerView animations - Androi

      本文链接:https://www.haomeiwen.com/subject/scankqtx.html