RecyclerView 中的列表中,当某个数据被移除刷新时,我们想让它对应的item移除时有个自定义动画效果怎么办?RecyclerView 对外提供了 setItemAnimator(ItemAnimator animator) 方法
ItemAnimator mItemAnimator = new DefaultItemAnimator();
public void setItemAnimator(ItemAnimator animator) {
if (mItemAnimator != null) {
mItemAnimator.endAnimations();
mItemAnimator.setListener(null);
}
mItemAnimator = animator;
if (mItemAnimator != null) {
mItemAnimator.setListener(mItemAnimatorListener);
}
}
ItemAnimator 类中有几个重要方法:
animateAppearance(): 当 item 出现在屏幕上时被调用(可能是add或move)。
animateDisappearance(): 当 item 消失在屏幕上时被调用(可能是remove或move)。
animateChange(): 在 调用 notifyItemChanged() 或 notifyDataSetChanged() 时被调用。
animatePersistence(): 在不是调用 notifyItemChanged() 和 notifyDataSetChanged() 的情况下布局发生改变时被调用。
runPendingAnimations(): 动画执行
isRunning(): 是否有动画要执行或正在执行。
dispatchAnimationsFinished(): 当全部动画执行完毕时被调用。
从RecyclerView 看到,有个默认的 DefaultItemAnimator 动画,如果有自定义的,则会替换默认的,看看 DefaultItemAnimator 是什么,它继承了 SimpleItemAnimator 这个抽象类,SimpleItemAnimator 中有些抽象方法,好些方法上都有注释,告诉我们这个方法的意思及要调用某些方法,DefaultItemAnimator 是个例子,里面有比较详细的细节及逻辑,我们可以写个子类,继承 SimpleItemAnimator,把一些关键方法先写个默认值
public abstract class ItemAnimatorAbstrc extends SimpleItemAnimator {
// item 移除回调
@Override
public boolean animateRemove(RecyclerView.ViewHolder holder) {
return false;
}
// item 添加回调
@Override
public boolean animateAdd(RecyclerView.ViewHolder holder) {
return false;
}
// 添加,移动更新时,其它 item 的动画执行
@Override
public boolean animateMove(RecyclerView.ViewHolder holder, int fromX, int fromY, int toX, int toY) {
return false;
}
// item 更新回调
@Override
public boolean animateChange(RecyclerView.ViewHolder oldHolder, RecyclerView.ViewHolder newHolder, int fromLeft, int fromTop, int toLeft, int toTop) {
return false;
}
// 控制执行动画的地方
@Override
public void runPendingAnimations() {
}
// 停止某个 item 的动画
@Override
public void endAnimation(RecyclerView.ViewHolder item) {
}
// 停止所有 item 动画
@Override
public void endAnimations() {
}
@Override
public boolean isRunning() {
return false;
}
}
我们写的这个类里面,没有抽象方法,但我还是给他加了个 abstract 来修饰,意思是它不能直接被使用,必须使用它的子类,这个类是个基类,仅仅是提供说明而已。一般来说,添加 item和移除item会比较多一点,所以重点看看 animateRemove()、 animateAdd()、 animateMove()、 runPendingAnimations() 这四个方法即可,比如说移除一个item,此时我们需要在
animateRemove() 方法中接收对应的 ViewHolder,然后在 runPendingAnimations() 方法中执行我们自定义的动画,并且在动画开始和结束时,分别调用 dispatchRemoveStarting() 和 dispatchAnimationsFinished() 方法,为什么不在 animateRemove() 方法中执行动画操作呢?因为我们可以存在多个动画同时执行的过程,如果按照上面的逻辑,item 在删除执行动画时,其它的 item 都已经自动往上位移了,体验很差,我们可以在 animateMove() 方法中让item位移还原,在 runPendingAnimations() 中与删除动画一起执行其他item的向上位移的操作。写个简单的item被删除操作
public class RemoveAnimator extends ItemAnimatorAbstrc {
List removeHolders = new ArrayList<>();
List removeAnimators = new ArrayList<>();
@Override
public boolean animateRemove(RecyclerView.ViewHolder holder) {
removeHolders.add(holder);
return true;
}
@Override
public void runPendingAnimations() {
if (!removeHolders.isEmpty()) {
for (RecyclerView.ViewHolder holder : removeHolders) {
remove(holder);
}
removeHolders.clear();
}
}
@Override
public boolean isRunning() {
return !(removeHolders.isEmpty() && removeAnimators.isEmpty());
}
private void remove(final RecyclerView.ViewHolder holder) {
removeAnimators.add(holder);
ObjectAnimator animator = ObjectAnimator.ofFloat(holder.itemView, "translationX", 0, holder.itemView.getMeasuredWidth());
animator.setDuration(500);
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(android.animation.Animator animation) {
dispatchRemoveStarting(holder);
}
@Override
public void onAnimationEnd(android.animation.Animator animation) {
removeAnimators.remove(holder);
dispatchRemoveFinished(holder);
if (!isRunning()) {
dispatchAnimationsFinished();
}
}
});
animator.start();
}
}
这样,一个粗糙的item删除动画就完成了。如果我们想让添加和删除效果好看一点,可以仿照 DefaultItemAnimator 来写一下, animateRemove()、 animateAdd()、 animateMove() 中向集合中添加元素时,一定要把返回值值为true,animateMove() 中是位移,我们为了不那么突兀,需要把item移动到它被删除前的位置。 runPendingAnimations() 中执行的顺序,先是删除,然后是move和change并行 ,最后是添加,对于移动和删除,我们把item当做一个普通的view,用属性动画即可,这样就实现了透明度及位置的变化,产生动画效果。有一点要注意,比如 move 时,在动画开始时,需要调用 dispatchMoveStarting()方法,在动画结束时需要调用 dispatchMoveFinished() 方法,并检查如果所有的动画都停止了,还需要调用 dispatchAnimationsFinished() 方法;add 和 remove 及 change 也是同样。
public class TestAnimator extends ItemAnimatorAbstrc {
List removeHolders = new ArrayList<>();
List removeAnimators = new ArrayList<>();
List moveHolders = new ArrayList<>();
List moveAnimators = new ArrayList<>();
ArrayList mPendingAdditions = new ArrayList<>();
ArrayList mAddAnimations = new ArrayList<>();
@Override
public boolean animateRemove(RecyclerView.ViewHolder holder) {
removeHolders.add(holder);
return true;
}
@Override
public boolean animateAdd(RecyclerView.ViewHolder holder) {
ViewCompat.setAlpha(holder.itemView, 0);
mPendingAdditions.add(holder);
return true;
}
@Override
public boolean animateMove(RecyclerView.ViewHolder holder, int fromX, int fromY, int toX, int toY) {
holder.itemView.setTranslationY(fromY - toY);
moveHolders.add(holder);
return true;
}
@Override
public boolean animateChange(RecyclerView.ViewHolder oldHolder, RecyclerView.ViewHolder newHolder, int fromLeft, int fromTop, int toLeft, int toTop) {
return false;
}
@Override
public void runPendingAnimations() {
if (!removeHolders.isEmpty()) {
for (RecyclerView.ViewHolder holder : removeHolders) {
remove(holder);
}
removeHolders.clear();
}
if (!moveHolders.isEmpty()) {
for (RecyclerView.ViewHolder holder : moveHolders) {
move(holder);
}
moveHolders.clear();
}
if (!mPendingAdditions.isEmpty()) {
for (RecyclerView.ViewHolder holder : mPendingAdditions) {
add(holder);
}
mPendingAdditions.clear();
}
}
@Override
public void endAnimation(RecyclerView.ViewHolder item) {
}
@Override
public void endAnimations() {
}
@Override
public boolean isRunning() {
return !(removeHolders.isEmpty() && removeAnimators.isEmpty()
&& moveHolders.isEmpty() && moveAnimators.isEmpty()
&& mPendingAdditions.isEmpty() && mAddAnimations.isEmpty()
);
}
private void remove(final RecyclerView.ViewHolder holder) {
removeAnimators.add(holder);
ObjectAnimator animator = ObjectAnimator.ofFloat(holder.itemView, "translationX", 0, holder.itemView.getMeasuredWidth());
animator.setDuration(500);
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(android.animation.Animator animation) {
dispatchRemoveStarting(holder);
}
@Override
public void onAnimationEnd(android.animation.Animator animation) {
removeAnimators.remove(holder);
dispatchRemoveFinished(holder);
dispatchFinishedWhenDone();
}
});
animator.start();
}
private void move(final RecyclerView.ViewHolder holder) {
moveAnimators.add(holder);
ObjectAnimator animator = ObjectAnimator.ofFloat(holder.itemView, "translationY", holder.itemView.getTranslationY(), 0);
animator.setDuration(500);
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(android.animation.Animator animation) {
dispatchMoveStarting(holder);
}
@Override
public void onAnimationEnd(android.animation.Animator animation) {
moveAnimators.remove(holder);
dispatchMoveFinished(holder);
dispatchFinishedWhenDone();
}
});
animator.start();
}
private void add(final RecyclerView.ViewHolder holder) {
final View view = holder.itemView;
final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view);
mAddAnimations.add(holder);
animation.alpha(1).setDuration(500).
setListener(new ViewPropertyAnimatorListener() {
@Override
public void onAnimationStart(View view) {
dispatchAddStarting(holder);
}
@Override
public void onAnimationCancel(View view) {
ViewCompat.setAlpha(view, 1);
}
@Override
public void onAnimationEnd(View view) {
animation.setListener(null);
mAddAnimations.remove(holder);
dispatchAddFinished(holder);
dispatchFinishedWhenDone();
}
}).start();
}
private void dispatchFinishedWhenDone() {
if (!isRunning()) {
dispatchAnimationsFinished();
}
}
}
这是个简化版的item添加移除动画,如果这个里面对应的方法都了解了,再去阅读 DefaultItemAnimator 代码就容易多了,里面做的更严谨,细节处理的更完美。