RecyclerView 源码分析(八)ItemAnimator的源码分析

转载自琼珶和予

ItemAnimator的源码分析

ItemAnimator作为RecyclerView的主要组成部分之一,其重要性自然不可言喻。因为ItemAnimator的存在,所以出现了很多精彩纷呈的动画,这使得RecyclerView更加惹人喜爱。因此,学习ItemAnimator的源码是势在必行的,因为我们了解原理之后,就可以自定义动画了,不再受人束缚。
  
本文参考资料:
recyclerview-animators
RecyclerView 源码分析(四) - RecyclerView的动画机制

本文打算才如下几个部分来分析ItemAnimator的源码:

  1. ItemAnimator类相关方法的分析和总结。
  2. SimpleItemAnimator相关代码的分析。
  3. DefaultItemAnimator相关代码的分析。
  4. 自定义一个ItemAnimator。

1. 概述

首先我们来对ItemAnimator的整个结构做一个简单的概述,方便大家理解。

通常来说,自定义一个ItemAnimator的过程是:ItemAnimator ->SimpleItemAnimator -> 自定义ItemAnimator。包括官方提供的DefaultItemAnimator也是这样来定义的,那这样来定义有好处的呢?这样定义,结构层次比较清晰,我们自定义ItemAnimator比较方便,只需要关注动画怎么实现就行。我们来看看这三层分别干了什么:

结构层次 作用
ItemAnimator 定义ItemAnimator的模板,自定义这里包括4个抽象方法,也是非常重要的抽象方法,分别是:animateDisappearance、animateAppearance、animatePersistence和animateChange
SimpleItemAnimator 实现了四个抽象方法,根据调用4个抽象方法的时机不同,所以会做不同的动画,所以又对外提供了4个抽象方法,分别是:animateRemove、animateAdd,animateMove和animateChange,分别对应删除、添加、移动和改变的动画
自定义ItemAnimator 主要实现4种操作的动画,也包括结束动画相关实现

而我们在自定义ItemAnimator时,只需要考虑第三层就OK,上面两层的逻辑谷歌爸爸已经帮我们实现了。自定义过ItemAnimator的同学应该都知道,尽管只关注第三层,但是实现还是那么麻烦,介于这个问题,我会提出一个非常简单的自定义itemAnimator的方案。

2. ItemAnimator

我们从上往下,看看每一层都为我们做了哪些事情,首先我们来了解一下ItemAnimator。ItemAnimator总的来说比较简单,我们来看看ItemAnimator几个方法

方法名 作用
animateDisappearance 抽象方法,供第二层实现此方法的调用时机是ItemView从当前的可见区域消失,其中包括:1.ItemView执行了remove操作;2. ItemView执行move操作移动到不可见区域。在此方法里面,根据不同的情况,执行move动画或者执行remove动画
animateAppearance 抽象方法,供第二层实现此方法的调用时机是ItemView出现在可见区域,其中包括:1. ItemView执行了add操作;2. ItemView执行move操作从不可见区域移动到可见区域。在此方法里面,根据不同的情况,执行add动画或者执行move动画
animatePersistence 抽象方法,供第二层实现此方法的调用时机是ItemView未进行任何操作。在此方法里面,根据不同的情况,会执行remove动画(比如说当前ItemView上面有一个ItemView执行了reomve操作)或者无任何动画
animateChange 抽象方法,供第二层实现此方法的调用时机是ItemView进行了change操作。在方法里面,会执行change动画

在ItemAnimator中,上面4个方法非常的重要,RecyclerView就是通过这四个方法来给每个ItemView添加不同的动画在这一层,我们需要掌握的就是,记住这4个方法的调用时机,这样我们在看SimpleItemAnimator代码时,才不会不知所措。

3. SimpleItemAnimator

就像在上面概述所说的一样,SimpleItemAnimator处于第二层,负责实现ItemAnimator的4个抽象方法,然后又提供了四种操作需要分别调用的抽象方法,这样做就更加细化了动画执行的情况,简化了自定义ItemAnimator的过程

在正式看SimpleItemAnimator的源码之前,我们先来看看SimpleItemAnimator几个方法的介绍

方法名 参数 说明
animateRemove ViewHolder 当当前的ItemView执行了remove操作需要执行remove动画时,会回调此方法
animateAdd ViewHolder 当当前的ItemView执行了add操作需要执行add动画时,会回调此方法
animateMove ViewHolder 当当前的ItemView执行了move操作,或者它之前有ItemView执行了remove操作或者add操作,会回调此方法
animateChange ViewHolder,oldHolder,ViewHolder newHolder, int fromLeft, int fromTop, int toLeft, int toTop 当当前的ItemView执行了change操作,会调用此方法

我们看到到了SimpleItemAnimator这一层,每个ItemView应该做什么动画,现在已经一清二楚了,不再像第一层里面那样,一个方法里面可能涉及到多种情况,每种情况可能执行不同的动画。

现在我们分别来看看SimpleItemAnimator对ItemAnimator的4个抽象方法的实现,看看他是怎么来判断一个ItemView执行何种动画的。

首先,我们来看一下animateDisappearance方法

@Override
public boolean animateDisappearance(@NonNull ViewHolder viewHolder,
        @NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo) {
   
    int oldLeft = preLayoutInfo.left;
    int oldTop = preLayoutInfo.top;
    View disappearingItemView = viewHolder.itemView;
    int newLeft = postLayoutInfo == null ? disappearingItemView.getLeft() : postLayoutInfo.left;
    int newTop = postLayoutInfo == null ? disappearingItemView.getTop() : postLayoutInfo.top;
    if (!viewHolder.isRemoved() && (oldLeft != newLeft || oldTop != newTop)) {
   
        disappearingItemView.layout(newLeft, newTop,
                newLeft + disappearingItemView.getWidth(),
                newTop + disappearingItemView.getHeight());
        if (DEBUG) {
   
            Log.d(TAG, "DISAPPEARING: " + viewHolder + " with view " + disappearingItemView);
        }
        return animateMove(viewHolder, oldLeft, oldTop, newLeft, newTop);
    } else {
   
        if (DEBUG) {
   
            Log.d(TAG, "REMOVED: " + viewHolder + " with view " + disappearingItemView);
        }
        return animateRemove(viewHolder);
    }
}

animateDisappearance方法表达的意思非常简单,首先判断当前ItemView是否需要执行的是move动画,如果是,那么就调用animateMove方法;如果不是的话,那么就调用animateRemove方法用来执行remove动画

在这个方法里面,remove操作我们理解,但是move操作是什么意思呢?首先我们得搞清楚animateDisappearance方法的调用时机,animateDisappearance方法表示在ItemView从可见状态变为不可见状态,这里包括:remove操作和ItemView从可见区域移动到不可见区域。所以在animateDisappearance方法里面,执行move动画并不意外。

然后,我们来看一下animateAppearance方法:

@Override
public boolean animateAppearance(@NonNull ViewHolder viewHolder,
        @Nullable ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo) {
   
    if (preLayoutInfo != null && (preLayoutInfo.left != postLayoutInfo.left
            || preLayoutInfo.top != postLayoutInfo.top)) {
   
        // slide items in if before/after locations differ
        if (DEBUG) {
   
            Log.d(TAG, "APPEARING: " + viewHolder + " with view " + viewHolder);
        }
        return animateMove(viewHolder, preLayoutInfo.left, preLayoutInfo.top,
                postLayoutInfo.left, postLayoutInfo.top);
    } else {
   
        if (DEBUG) {
   
            Log.d(TAG, "ADDED: " + viewHolder + " with view " + viewHolder);
        }
        return animateAdd(viewHolder);
    }
}

animateAppearance方法表示在ItemView从不可见状态变为可见状态,所以这里包括add操作和move操作。move操作表示的意思跟animateDisappearance方法的差不多。

然后,我们再来看看animatePersistence方法:

public boolean animatePersistence(@NonNull ViewHolder viewHolder,
        @NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo) {
   
    if (preInfo.left != postInfo.left || preInfo.top != postInfo.top) {
   
        if (DEBUG) {
   
            Log.d(TAG, "PERSISTENT: " + viewHolder
                    + " with view " + viewHolder.itemView);
        }
        return animateMove(viewHolder,
                preInfo.left, preInfo.top, postInfo.left, postInfo.top);
    }
    dispatchMoveFinished(viewHolder);
    return false;
}

animatePersistence方法比其他方法都简单,这里只进行了move动画当然如果不执行任何动画,这里会返回false,并且会调用dispatchMoveFinished方法,这是基本要求,当每个动画执行完毕之后,都是调用相关方法来通知动画执行结束了

最后,我们再来看看animateChange方法

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值