ItemAnimator
做为RecyclerView
的主要组成部分之一,其重要性天然不可言喻。由于ItemAnimator
的存在,因此出现了不少精彩纷呈的动画,这使得RecyclerView
更加惹人喜好。所以,学习ItemAnimator
的源码是势在必行的,由于咱们了解原理以后,就能够自定义动画了,再也不受人束缚。git
本文参考资料:github
本文打算才以下几个部分来分析ItemAnimator
的源码:数组
ItemAnimator
类相关方法的分析和总结。SimpleItemAnimator
相关代码的分析。DefaultItemAnimator
相关代码的分析。- 自定义一个
ItemAnimator
。
首先咱们来对ItemAnimator
的整个结构作一个简单的概述,方便你们理解。bash
一般来讲,自定义一个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
的方案。app
咱们从上往下,看看每一层都为咱们作了哪些事情,首先咱们来了解一下ItemAnimator
。ItemAnimator
总的来讲比较简单,咱们来看看ItemAnimator
几个方法:ide
方法名 | 做用 |
---|---|
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
代码时,才不会不知所措。源码分析
就像在上面概述所说的同样,SimpleItemAnimator
处于第二层,负责实现ItemAnimator
的4个抽象方法,而后又提供了四种操做须要分别调用的抽象方法,这样作就更加细化了动画执行的状况,简化了自定义ItemAnimator
的过程。post
在正式看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
方法:
@Override
public boolean animateChange(@NonNull ViewHolder oldHolder, @NonNull ViewHolder newHolder,
@NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo) {
if (DEBUG) {
Log.d(TAG, "CHANGED: " + oldHolder + " with view " + oldHolder.itemView);
}
final int fromLeft = preInfo.left;
final int fromTop = preInfo.top;
final int toLeft, toTop;
if (newHolder.shouldIgnore()) {
toLeft = preInfo.left;
toTop = preInfo.top;
} else {
toLeft = postInfo.left;
toTop = postInfo.top;
}
return animateChange(oldHolder, newHolder, fromLeft, fromTop, toLeft, toTop);
}
复制代码
animateChange
方法更加的简单,最后始终调用了animateChange
抽象方法。
咱们在看DefaultItemAnimator
的源码以前,先来看看它有哪些成员变量:
private static TimeInterpolator sDefaultInterpolator;
private ArrayList<ViewHolder> mPendingRemovals = new ArrayList<>();
private ArrayList<ViewHolder> mPendingAdditions = new ArrayList<>();
private ArrayList<MoveInfo> mPendingMoves = new ArrayList<>();
private ArrayList<ChangeInfo> mPendingChanges = new ArrayList<>();
ArrayList<ArrayList<ViewHolder>> mAdditionsList = new ArrayList<>();
ArrayList<ArrayList<MoveInfo>> mMovesList = new ArrayList<>();
ArrayList<ArrayList<ChangeInfo>> mChangesList = new ArrayList<>();
ArrayList<ViewHolder> mAddAnimations = new ArrayList<>();
ArrayList<ViewHolder> mMoveAnimations = new ArrayList<>();
ArrayList<ViewHolder> mRemoveAnimations = new ArrayList<>();
ArrayList<ViewHolder> mChangeAnimations = new ArrayList<>();
复制代码
在DefaultItemAnimator
的内部,成员变量主要分为4个部分。这其中,sDefaultInterpolator
就是给ItemView
设置的一个动画插值器对象,在DefaultItemAnimator
内部,这个插值器是AccelerateDecelerateInterpolator
插值器;而后第二部分mPendingxxx
数组,这部分就是用来存储每一个ItemView
须要作动画的相关信息,例如,move动画就须要移动的起始位置和终点位置,这部分的数组才是作动画的真正作动画须要的;第三部分和第四部分是mXXXList
和mXXXAnimations
,一般来讲,都是用于结束动画的,本文后面会简单的分析他们。
简单的了解这四部分的成员变量以后,如今咱们重点看一下四个抽象方法的实现。
@Override
public boolean animateRemove(final ViewHolder holder) {
resetAnimation(holder);
mPendingRemovals.add(holder);
return true;
}
复制代码
animateRemove
方法的实现很是简单,就是往mPendingRemovals
数组里面添加一个元素。
而后,咱们再来看一下animateAdd
方法:
@Override
public boolean animateAdd(final ViewHolder holder) {
resetAnimation(holder);
holder.itemView.setAlpha(0);
mPendingAdditions.add(holder);
return true;
}
复制代码
animateAdd
方法的实现也是很是简单。这里咱们看到有一个操做就是holder.itemView.setAlpha(0)
,咱们都知道,在DefaultItemAnimator
中,add动画是一个渐现的过程,这里先将ItemView
的alpha值设置0就容易理解了。
而后,咱们在来看一下animateMove
方法的实现:
@Override
public boolean animateMove(final ViewHolder holder, int fromX, int fromY,
int toX, int toY) {
final View view = holder.itemView;
fromX += (int) holder.itemView.getTranslationX();
fromY += (int) holder.itemView.getTranslationY();
resetAnimation(holder);
int deltaX = toX - fromX;
int deltaY = toY - fromY;
if (deltaX == 0 && deltaY == 0) {
dispatchMoveFinished(holder);
return false;
}
if (deltaX != 0) {
view.setTranslationX(-deltaX);
}
if (deltaY != 0) {
view.setTranslationY(-deltaY);
}
mPendingMoves.add(new MoveInfo(holder, fromX, fromY, toX, toY));
return true;
}
复制代码
相对来讲,animateMove
的实现比其余操做都要复杂一些,可是再怎么复杂,其实就作了两件事:
- 根据状况,来设置
View
的translationX
或者translationY
。- 向
mPendingMoves
数组里面添加一个MoveInfo
,用于move动画使用。
最后,咱们在来看看animateChange
方法的实现:
public boolean animateChange(ViewHolder oldHolder, ViewHolder newHolder,
int fromX, int fromY, int toX, int toY) {
if (oldHolder == newHolder) {
// Don't know how to run change animations when the same view holder is re-used. // run a move animation to handle position changes. return animateMove(oldHolder, fromX, fromY, toX, toY); } final float prevTranslationX = oldHolder.itemView.getTranslationX(); final float prevTranslationY = oldHolder.itemView.getTranslationY(); final float prevAlpha = oldHolder.itemView.getAlpha(); resetAnimation(oldHolder); int deltaX = (int) (toX - fromX - prevTranslationX); int deltaY = (int) (toY - fromY - prevTranslationY); // recover prev translation state after ending animation oldHolder.itemView.setTranslationX(prevTranslationX); oldHolder.itemView.setTranslationY(prevTranslationY); oldHolder.itemView.setAlpha(prevAlpha); if (newHolder != null) { // carry over translation values resetAnimation(newHolder); newHolder.itemView.setTranslationX(-deltaX); newHolder.itemView.setTranslationY(-deltaY); newHolder.itemView.setAlpha(0); } mPendingChanges.add(new ChangeInfo(oldHolder, newHolder, fromX, fromY, toX, toY)); return true; } 复制代码
而change操做比较特殊,哪里特殊呢?主要有两点:
- 它涉及到从旧状态变成新状态,因此须要考虑两个
ItemView
的存在。- 在作change操做时,可能会作move操做。因此在这里还设置了
ItemView
的translation
。
这里只是简单的介绍了ItemView
可能同时进行change操做和move操做,待会在将动画实现时,咱们来看一下DefaultItemAnimator
是怎么实现两个动画同时进行的。
咱们往数组里面添加了不少须要作动画的元素,可是何时开始执行这些动画呢?其实咱们在RecyclerView 源码分析(四) - RecyclerView的动画机制这篇文章里面已经分析了RecyclerView
是怎么开始动画的。其实就是回调了ItemAnimator
的一个方法--runPendingAnimations
方法,以前咱们添加的全部动画都在方法里面执行。咱们在看runPendingAnimations
方法以前,咱们有几个问题,咱们带着问题去看思路会更加的清晰,一共两个问题:
DefaultItemAnimator
是怎么实现每种操做的动画呢?- 咱们知道,在
RecyclerView
中,某些操做的动画是时序,好比说,必须在remove动画执行完毕以后,才会执行move动画,这个又是怎么实现呢?
经过阅读runPendingAnimations
方法的源码,咱们能够将它的源码分为4个部分,分别以下:
- remove动画的执行
- move动画的执行
- change动画的执行
- add动画的执行
咱们分别来看看这四部分的代码。
// First, remove stuff
for (ViewHolder holder : mPendingRemovals) {
animateRemoveImpl(holder);
}
mPendingRemovals.clear();
复制代码
经过上面的代码,咱们知道,remove动画的实现关键在于animateRemoveImpl
方法,咱们来看看animateRemoveImpl
方法的实现:
private void animateRemoveImpl(final 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();
}
复制代码
在这里,咱们知道,ItemView
的remove动画是经过ViewPropertyAnimator
来实现的。在这里咱们须要注意几点:
- 在动画开始以前,往
mRemoveAnimations
数组里面添加了一个元素,主要是用于结束动画的操做。当咱们在结束动画时,发现mRemoveAnimations
数组里面还有元素,表示还有remove动画没有执行完毕,因此结束它。- 在
onAnimationEnd
方法里面,咱们分别调用了dispatchRemoveFinished
方法和dispatchFinishedWhenDone
。这两步操做是必须的,并且注意他们的时序。
至于ViewPropertyAnimator
实现动画为何须要这样来讲,这不是本文的重点,这里就不介绍了,你们有兴趣能够去简单学习一下。
if (movesPending) {
final ArrayList<MoveInfo> moves = new ArrayList<>();
moves.addAll(mPendingMoves);
mMovesList.add(moves);
mPendingMoves.clear();
Runnable mover = new Runnable() {
@Override
public void run() {
for (MoveInfo moveInfo : moves) {
animateMoveImpl(moveInfo.holder, moveInfo.fromX, moveInfo.fromY,
moveInfo.toX, moveInfo.toY);
}
moves.clear();
mMovesList.remove(moves);
}
};
if (removalsPending) {
View view = moves.get(0).holder.itemView;
ViewCompat.postOnAnimationDelayed(view, mover, getRemoveDuration());
} else {
mover.run();
}
}
复制代码
这部分的代码相对来讲要复杂一点点,咱们来简单的分析一下。上面的代码,咱们须要注意如以下几点:
- 将move动画相关的动画元素原封不动的添加到
mMovesList
数组里面。这样作的目的是,由于move动画的开始有延迟,得等待remove
动画执行完毕以后才执行。因此,存在咱们在结束动画时,move动画尚未开始执行的状况,因此得先添加进去,以便结束move动画。- 从这里咱们就能够知道,
DefaultItemAnimator
是怎么解决动画的时序问题。这里经过ViewCompat
的postOnAnimationDelayed
来作一个延迟执行,保证rmove动画执行完毕才执行move动画。
而后,咱们在来看看animateMoveImpl
方法是怎么实现move动画的:
void animateMoveImpl(final ViewHolder holder, int fromX, int fromY, int toX, int toY) {
final View view = holder.itemView;
final int deltaX = toX - fromX;
final int deltaY = toY - fromY;
if (deltaX != 0) {
view.animate().translationX(0);
}
if (deltaY != 0) {
view.animate().translationY(0);
}
// TODO: make EndActions end listeners instead, since end actions aren't called when // vpas are canceled (and can't end them. why?)
// need listener functionality in VPACompat for this. Ick.
final ViewPropertyAnimator animation = view.animate();
mMoveAnimations.add(holder);
animation.setDuration(getMoveDuration()).setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animator) {
dispatchMoveStarting(holder);
}
@Override
public void onAnimationCancel(Animator animator) {
if (deltaX != 0) {
view.setTranslationX(0);
}
if (deltaY != 0) {
view.setTranslationY(0);
}
}
@Override
public void onAnimationEnd(Animator animator) {
animation.setListener(null);
dispatchMoveFinished(holder);
mMoveAnimations.remove(holder);
dispatchFinishedWhenDone();
}
}).start();
}
复制代码
animateMoveImpl
方法实现move动画其实跟remove的实现差很少,都是经过ViewPropertyAnimator
来实现的,因此这里就再也不分析了。
而后咱们再来看看change动画的实现:
if (changesPending) {
final ArrayList<ChangeInfo> changes = new ArrayList<>();
changes.addAll(mPendingChanges);
mChangesList.add(changes);
mPendingChanges.clear();
Runnable changer = new Runnable() {
@Override
public void run() {
for (ChangeInfo change : changes) {
animateChangeImpl(change);
}
changes.clear();
mChangesList.remove(changes);
}
};
if (removalsPending) {
ViewHolder holder = changes.get(0).oldHolder;
ViewCompat.postOnAnimationDelayed(holder.itemView, changer, getRemoveDuration());
} else {
changer.run();
}
}
复制代码
上面的代码跟move
动画那部分的实现都差很少,这里就再也不分析了,咱们这里重点是看animateChangeImpl
方法:
void animateChangeImpl(final ChangeInfo changeInfo) {
final ViewHolder holder = changeInfo.oldHolder;
final View view = holder == null ? null : holder.itemView;
final ViewHolder newHolder = changeInfo.newHolder;
final View newView = newHolder != null ? newHolder.itemView : null;
if (view != null) {
final ViewPropertyAnimator oldViewAnim = view.animate().setDuration(
getChangeDuration());
mChangeAnimations.add(changeInfo.oldHolder);
oldViewAnim.translationX(changeInfo.toX - changeInfo.fromX);
oldViewAnim.translationY(changeInfo.toY - changeInfo.fromY);
oldViewAnim.alpha(0).setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animator) {
dispatchChangeStarting(changeInfo.oldHolder, true);
}
@Override
public void onAnimationEnd(Animator animator) {
oldViewAnim.setListener(null);
view.setAlpha(1);
view.setTranslationX(0);
view.setTranslationY(0);
dispatchChangeFinished(changeInfo.oldHolder, true);
mChangeAnimations.remove(changeInfo.oldHolder);
dispatchFinishedWhenDone();
}
}).start();
}
if (newView != null) {
final ViewPropertyAnimator newViewAnimation = newView.animate();
mChangeAnimations.add(changeInfo.newHolder);
newViewAnimation.translationX(0).translationY(0).setDuration(getChangeDuration())
.alpha(1).setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animator) {
dispatchChangeStarting(changeInfo.newHolder, false);
}
@Override
public void onAnimationEnd(Animator animator) {
newViewAnimation.setListener(null);
newView.setAlpha(1);
newView.setTranslationX(0);
newView.setTranslationY(0);
dispatchChangeFinished(changeInfo.newHolder, false);
mChangeAnimations.remove(changeInfo.newHolder);
dispatchFinishedWhenDone();
}
}).start();
}
}
复制代码
change跟其余比较起来,实现起来就复杂的多。咱们来简单的分析一下:
- 在
animateChangeImpl
方法里面,主要是有两个动画在执行,一个是旧的ItemView
渐隐动画,一个是新的ItemView
渐现动画。- 在change动画里面,同时包含了两部分属性的动画,一个是位置的渐变,一个是透明度的渐变。从这里,咱们就能够看到,一个在作change动画的
ItemView
也有可能作move
动画。因此,在runPendingAnimations
方法里面,4个步骤独立执行,一个ViewHolder
不可能同时走了其中两个或者两个以上的步骤。
这里add动画没有特殊的操做,就是简单的渐现,咱们就不作多余的介绍了。
你们想要自定以一个ItemAnimator
,第一个想法就是先去网上搜索一下自定以ItemAnimator
的基本步骤。我相信你们搜索到的都是一开始就叫咱们实现不少不少的方法,又不知道这些的做用,有些可能还简单介绍了一下每一个方法的做用,可是根本不知道这个方法的具体实现。为何须要这么来实现动画呢?这些都是网上大多数文章没有介绍清楚的。
这里,我提出一种自定义ItemAnimator
的解决方案,有多是目前最简单的方案。
一般上面的源码的学习,咱们了解了DefaultItemAnimator
的实现原理。因此,咱们自定义一个ItemAnimator
,能够参考DefaultItemAnimator
的实现。从而,咱们自定义一个ItemAnimator
,须要解决以下几个问题:
- 须要考虑动画执行的时序。
- 须要考虑结束动画。
- 实现每种动画的实现逻辑。
从上面的问题中,咱们发现问题1和问题2,DefaultItemAnimator
已经帮咱们实现了,咱们只须要解决问题3就好了。因此,个人解决方案就是:**将DefaultItemAnimator
的拷贝出来,咱们只须要改写animateRemoveImpl
、animateAddImpl
、animateMoveImpl
和animateChangeImpl
这四个方法就好了。**一般来讲,change动画和move动画,咱们都不会改写,因此重点在于add动画和remove动画。
如今,我来给你们看一下我自定义的一个动画,效果以下:
关于代码部分,我几乎没有动DefaultItemAnimator
的架构,只是改写了animateRemoveImpl
方法和animateAddImpl
方法,咱们来看看:
private void animateRemoveImpl(final ViewHolder holder) {
final View view = holder.itemView;
final ViewPropertyAnimator animation = view.animate();
mRemoveAnimations.add(holder);
animation.setDuration(getRemoveDuration()).translationX(view.getRootView().getWidth()).setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animator) {
dispatchRemoveStarting(holder);
}
@Override
public void onAnimationEnd(Animator animator) {
animation.setListener(null);
view.setTranslationX(0);
dispatchRemoveFinished(holder);
mRemoveAnimations.remove(holder);
dispatchFinishedWhenDone();
}
}).start();
}
void animateAddImpl(final ViewHolder holder) {
final View view = holder.itemView;
final ViewPropertyAnimator animation = view.animate();
mAddAnimations.add(holder);
animation.translationX(0).setDuration(getAddDuration())
.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animator) {
dispatchAddStarting(holder);
}
@Override
public void onAnimationCancel(Animator animator) {
view.setAlpha(0);
}
@Override
public void onAnimationEnd(Animator animator) {
animation.setListener(null);
dispatchAddFinished(holder);
mAddAnimations.remove(holder);
dispatchFinishedWhenDone();
}
}).start();
}
复制代码
实现的逻辑也是很是简单,就是将DefaultItemAnimator
的alpha变化改成了translationX
的变化。如上所示,自定义一个ItemAnimator
就是这么简单!
若是还有不懂的同窗,能够去个人github去下载Demo:ItemAnimatorDemo。
关于ItemAnimator
的源码分析到此就已经结束了,我在这里作一个简单的总结。
- 理解
ItemAnimator
的原理以前,咱们最好先了解它的三层模型,分别是ItemAnimator
、SimpleItemAnimator
和DefaultItemAnimator
,咱们须要搞清楚每一层到底为咱们作了哪些事情。- 自定义
ItemAnimator
咱们只须要拷贝DefaultItemAnimator
的代码,而后根据本身的要求分别改写animateRemoveImpl
、animateAddImpl
、animateMoveImpl
和animateChangeImpl
这四个方法就好了。
到此为止,RecyclerView
源码分析系列的文章就结束了。经过这系列的文章中,咱们全方位立体的了解了RecyclerView
的各个方面,使得咱们对RecyclerView
的理解更上一层楼了。固然这个过程当中确定有地方缺乏了,就好比说:ItemTouchHelper
或者ItemDecoration
,这部分的内容相对来讲简单一点,后续我会推出一个RecyclerView
的拓展系列,来补充这部分的内容。
人非圣贤,孰能无过!各位大佬在阅读过程当中若是发现错误或者有疑惑,均可以提出来,欢迎大佬们斧正!