android 自定义recyclerview,Android—RecyclerView进阶(3)—ItemAnimator分析及自定义

我的CSDN: ListerCi

我的简书: 东方未曦

俗话说,好看的皮囊千篇一律,有趣的灵魂万里挑一。但是对于我们这些俗人来说,肯定是选择好看的皮囊,咱们的用户也是如此。你看看应用市场上那些花枝招展的APP,哪个不是用上了五花八门的动画效果,就算你的内在安全省电性能好,没点儿花招可留不住花心的用户。所以我们今天就来看看怎么实现让用户眼前一亮的动画,当然原理也很重要,因此源码分析必不可少,本文的源码分析主要聚焦于动画是怎么触发的,以及动画是怎么实现的。

一、动画的触发与实现

当Adapter中的数据发生变化时,我们通过notifyItemXXX()等方法通知RecyclerView来改变数据的展示,这个过程必然伴随新的layout()。如果在layout()后直接显示新数据,效果比较僵硬,因此需要通过动画来制造良好的用户体验。

那么,为了实现动画,RecyclerView又额外做了哪些工作呢?抽象上来讲,RecyclerView实现动画的步骤如下。

① 数据发生改变时,保存当前的item信息为preInfo

② 根据新的数据Layout

③ Layout完毕,保存当前的item信息为postInfo

④ 根据preInfo和postInfo判断动画类型并交给ItemAnimator执行

可以发现,前3步保存了执行动画所需要的信息,最后整体交给ItemAnimator来执行动画。前3步涉及到内容较为复杂,我们先从简单的开始分析,来看ItemAnimator是怎么实现动画的。

1.1 动画的实现

由于RecyclerView设计时的低耦合性,ItemAnimator只需要关注怎么执行动画即可,其逻辑并不复杂。RecyclerView为我们实现了DefaultItemAnimator,在不设置动画的情况下默认使用它,其中实现了4个针对item的动画,分别为Remove、Move、Add和Change。以Remove动画为例,当一个item被移出RecyclerView时,DefaultItemAnimator中的animateRemove(holder)方法就会被调用,但是并没有马上开始执行动画,而是将动画添加到了mPendingRemovals中,这是一个待执行的Romove动画List。

@Override

public boolean animateRemove(final RecyclerView.ViewHolder holder) {

resetAnimation(holder);

mPendingRemovals.add(holder);

return true;

}

看一下DefaultItemAnimator的成员变量,原来有4个List分别存储4种动画。

private ArrayList mPendingRemovals = new ArrayList<>();

private ArrayList mPendingAdditions = new ArrayList<>();

private ArrayList mPendingMoves = new ArrayList<>();

private ArrayList mPendingChanges = new ArrayList<>();

当RecyclerView要执行动画时,ItemAnimator的runPendingAnimations()方法会被调用,DefaultItemAnimator重写后的方法如下,为了便于阅读,添加了注释并省略了部分代码。

@Override

public void runPendingAnimations() {

boolean removalsPending = !mPendingRemovals.isEmpty();

boolean movesPending = !mPendingMoves.isEmpty();

boolean changesPending = !mPendingChanges.isEmpty();

boolean additionsPending = !mPendingAdditions.isEmpty();

// 判断是否有动画需要执行

if (!removalsPending && !movesPending && !additionsPending && !changesPending) {

return;

}

// 执行Remove动画

for (RecyclerView.ViewHolder holder : mPendingRemovals) {

animateRemoveImpl(holder); // 实际执行Remove动画的方法

}

mPendingRemovals.clear();

// Move动画

if (movesPending) {

// 注意: Move动画并不是马上执行,会放入一个Runnable

Runnable mover = new Runnable() {

@Override

public void run() {

for (MoveInfo moveInfo : moves) {

animateMoveImpl(moveInfo.holder, moveInfo.fromX, moveInfo.fromY,

moveInfo.toX, moveInfo.toY);

}

}

};

// 如果有Remove动画,就在Remove动画结束之后执行Move动画

// 如果没有Remove动画就马上执行

if (removalsPending) {

View view = moves.get(0).holder.itemView;

ViewCompat.postOnAnimationDelayed(view, mover, getRemoveDuration());

} else {

mover.run();

}

}

// Change动画,与Move动画一起执行

if (changesPending) {

// ......

Runnable changer = new Runnable() {

@Override

public void run() {

for (ChangeInfo change : changes) {

animateChangeImpl(change);

}

}

};

if (removalsPending) {

RecyclerView.ViewHolder holder = changes.get(0).oldHolder;

ViewCompat.postOnAnimationDelayed(holder.itemView, changer, getRemoveDuration());

} else {

changer.run();

}

}

// 最后执行Add动画

if (additionsPending) {

// ......

Runnable adder = new Runnable() {

@Override

public void run() {

for (RecyclerView.ViewHolder holder : additions) {

animateAddImpl(holder);

}

additions.clear();

mAdditionsList.remove(additions);

}

};

// 在Remove、Move、Change动画都完成之后开始执行Add动画

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);

} else {

adder.run();

}

}

}

我们发现动画的执行是有顺序的,Remove动画首先执行,之后Move和Change动画同时开始,等这3个动画全部结束之后开始执行Add动画。以RecyclerView删除一个item为例,动画如下。

gif-Remove动画.gif

我们发现动画有2段,首先是被删除item的Remove动画,等到完全不可见之后,下方的多个item同时执行向上的Move动画。相对的,如果向RecyclerView中添加一个item,会先执行item向下的Move动画,再执行插入item的Add动画。

在runPendingAnimations()中真正执行动画的是animateRemoveImpl()这样的方法,来看一下它是怎么实现的,这也是我们今后自定义动画的关键。

private void animateRemoveImpl(final RecyclerView.ViewHolder holder) {

final View view = holder.itemView;

final ViewPropertyAnimator animation = view.animate();

mRemoveAnimations.add(holder);

animation.setDuration(getRemoveDuration()).alpha(0).setListener(

new AnimatorListenerAdapter() {

@Override

public void onAnimationStart(Animator animator) {

dispatchRemoveStarting(holder);

}

@Override

public void onAnimationEnd(Animator animator) {

animation.setListener(null);

view.setAlpha(1);

dispatchRemoveFinished(holder);

mRemoveAnimations.remove(holder);

dispatchFinishedWhenDone();

}

}).start();

}

Remove动画很简单,通过ViewPropertyAnimator实现一个透明度到0的属性动画,不过要记得在动画开始和结束时调用dispatchRemoveStarting()和dispatchRemoveFinished()方法。相对的,Add动画就是透

你可能感兴趣的:(android,自定义recyclerview)