注册

RecyclerView列表动画

一 ItemAnimator的使用

触发删除动画

mDatas.remove(position);
notifyItemRemoved(position)

触发添加动画

mDatas.add(position,data);
notifyItemInserted(position)

触发改变动画

mDatas.set(position,newData);
notifyItemChanged(position)

使用简单动画

//RecyclerView.LayoutManager.java
public boolean supportsPredictiveItemAnimations() {
return false;
}

添加/删除/改变都是渐入渐出动画:

f8b64fc378794d3d87844e3586fa661b~tplv-k3u1fbpfcp-no-mark:1280:960:0:0.awebp

使用预测性动画

//RecyclerView.LayoutManager.java
public boolean supportsPredictiveItemAnimations() {
return true;
}
9f97f5a3958e4c1f876e87b3390d598d~tplv-k3u1fbpfcp-no-mark:1280:960:0:0.awebp

二 配合LayoutAnimation使用

RecyclerView和普通的ViewGroup一样支持LayoutAnimation.使用起来比较简单,设置RecyclerView的 animateLayoutChangeslayoutAnimation属性即可.

案例

layout_animation_recyclerview.gif
图片出处:Layout animations on RecyclerView

layoutanimation1.gif
图片出处:RecyclerView 与 LayoutAnimation 实现的进入动画(一 ): List

三 自定义RecyclerView列表动画

案例学习:自定义change动画

效果:

3ddf836e90f647e3b18fbbd79385630d~tplv-k3u1fbpfcp-no-mark:1280:960:0:0.awebp

代码实现:

1.新建MyChangeAnimator类继承DefaultItemAnimator

private class MyChangeAnimator extends DefaultItemAnimator {
}

2.重写DefaultItemAnimatorcanReuseUpdatedViewHolder()方法.

@Override
public boolean canReuseUpdatedViewHolder(RecyclerView.ViewHolder viewHolder) {
//change动画在同一个ItemHolder上执行
return true;
}

3.新建ColorTextInfo类继承ItemHolderInfo,新增两个字段记录item的颜色和文字.

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

4.重写DefaultItemAnimatorobtainHolderInfo()方法,新建一个ColorTextInfo对象返回.

@Override
public ItemHolderInfo obtainHolderInfo() {
return new ColorTextInfo();
}
  1. 重写DefaultItemAnimatorrecordPreLayoutInformation()recordPostLayoutInformation()方法.在item变化前和变化后记录颜色和文字信息.
public ItemHolderInfo recordPreLayoutInformation(RecyclerView.State state,
RecyclerView.ViewHolder viewHolder, int changeFlags, List<Object> payloads) {
ColorTextInfo info = (ColorTextInfo) super.recordPreLayoutInformation(state, viewHolder,changeFlags, payloads);
//记录item变化前的颜色和文字
return getItemHolderInfo((MyViewHolder) viewHolder, info);
}

public ItemHolderInfo recordPostLayoutInformation(@NonNull RecyclerView.State state,
@NonNull RecyclerView.ViewHolder viewHolder) {
ColorTextInfo info = (ColorTextInfo) super.recordPostLayoutInformation(state, viewHolder);
//记录item变化后的颜色和文字
return getItemHolderInfo((MyViewHolder) viewHolder, info);
}

//从viewHolder上获取ColorTextInfo信息
private ItemHolderInfo getItemHolderInfo(MyViewHolder viewHolder, ColorTextInfo info) {
final MyViewHolder myHolder = viewHolder;
final int bgColor = ((ColorDrawable) myHolder.container.getBackground()).getColor();
info.color = bgColor;
info.text = (String) myHolder.textView.getText();
return info;
}

6.重写DefaultItemAnimatoranimateChange()方法,执行自定义的change动画

顺序执行
顺序执行
开始change动画
旧的颜色到黑色的渐变
旧的文字绕x轴旋转0->90度
黑色到新的颜色的渐变
新的文字绕x轴旋转-90->0度
change动画结束
public boolean animateChange(@NonNull final RecyclerView.ViewHolder oldHolder,
@NonNull final RecyclerView.ViewHolder newHolder,
@NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo) {

...

final MyViewHolder viewHolder = (MyViewHolder) newHolder;

//读取change前后的颜色和文字信息
ColorTextInfo oldInfo = (ColorTextInfo) preInfo;
ColorTextInfo newInfo = (ColorTextInfo) postInfo;
int oldColor = oldInfo.color;
int newColor = newInfo.color;
final String oldText = oldInfo.text;
final String newText = newInfo.text;

LinearLayout newContainer = viewHolder.container;
final TextView newTextView = viewHolder.textView;


// 构造旧的颜色到黑色的渐变动画
ObjectAnimator fadeToBlack = null, fadeFromBlack;
fadeToBlack = ObjectAnimator.ofInt(newContainer, "backgroundColor",
startColor, Color.BLACK);
fadeToBlack.setEvaluator(mColorEvaluator);

// 构造黑色到新的颜色的渐变动画
fadeFromBlack = ObjectAnimator.ofInt(newContainer, "backgroundColor",
Color.BLACK, newColor);

// 这两个渐变动画顺序执行
AnimatorSet bgAnim = new AnimatorSet();
bgAnim.playSequentially(fadeToBlack, fadeFromBlack);

//构造旧的文字绕x轴旋转0->90度的动画
ObjectAnimator oldTextRotate = null, newTextRotate;
    oldTextRotate = ObjectAnimator.ofFloat(newTextView, View.ROTATION_X, 0, 90);
    oldTextRotate.setInterpolator(mAccelerateInterpolator);
    oldTextRotate.addListener(new AnimatorListenerAdapter() {
     boolean mCanceled = false;
     @Override
     public void onAnimationStart(Animator animation) {
     //动画开始时显示旧的文字
     newTextView.setText(oldText);
     }

     @Override
     public void onAnimationCancel(Animator animation) {
     mCanceled = true;
     }

     @Override
     public void onAnimationEnd(Animator animation) {
     if (!mCanceled) {
     //动画结束时显示新的文字
     newTextView.setText(newText);
     }
     }
    });

// 构造新的文字绕x轴旋转-90->0度的动画
newTextRotate = ObjectAnimator.ofFloat(newTextView, View.ROTATION_X, -90, 0);
newTextRotate.setInterpolator(mDecelerateInterpolator);

//两个旋转动画顺序执行
AnimatorSet textAnim = new AnimatorSet();
textAnim.playSequentially(oldTextRotate, newTextRotate);

// 渐变的动画和旋转的动画同时执行
AnimatorSet changeAnim = new AnimatorSet();
changeAnim.playTogether(bgAnim, textAnim);
changeAnim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
dispatchAnimationFinished(newHolder);
...
}
});
changeAnim.start();

return true;
}

7.设置RecyclerViewItemAnimator为自定义的MyChangeAnimator

mRecyclerView.setItemAnimator(mChangeAnimator);

这是简化后的代码.官方完整的demo里还包括了边界情况的处理,处理了上一次change动画没执行完时触发新的change动画的情况.
代码地址:github.com/android/vie…

自定义add和del动画

目标效果:

d7076ad4f61d452da90d1cdb4c1f2c01~tplv-k3u1fbpfcp-no-mark:1280:960:0:0.awebp

代码实现:

1.拷贝DefaultItemAnimator源码,命名为DefaultItemAnimatorOpen.java

2.重写添加和删除的动画实现.(这一步需要修改DefaultItemAnimatorOpen中一些方法和属性的可见性为protected).

参考DefaultItemAnimatorOpen中对应方法的实现,修改动画相关的代码即可:

class CustomAddDelAnimation : DefaultItemAnimatorOpen() {


override fun animateAdd(holder: RecyclerView.ViewHolder): Boolean {
//重写item添加的动画
resetAnimation(holder)
//将新增的item绕x轴旋转90度
holder.itemView.rotationX = 90f
mPendingAdditions.add(holder)
return true
}

override fun animateAddImpl(holder: RecyclerView.ViewHolder) {
//添加item时执行绕x轴90->0度的旋转
mAddAnimations.add(holder)
holder.itemView.animate().apply {
rotationX(0f)
duration = addDuration
interpolator = DecelerateInterpolator(3f)
setListener(object : AnimatorListenerAdapter() {

override fun onAnimationStart(animation: Animator?) {
dispatchAddStarting(holder)
}

override fun onAnimationCancel(animation: Animator?) {
ViewHelper.clear(holder.itemView)
}

override fun onAnimationEnd(animation: Animator) {
ViewHelper.clear(holder.itemView)
dispatchAddFinished(holder)
mAddAnimations.remove(holder)
dispatchFinishedWhenDone()
}
})
}.start()
}

override fun animateRemove(holder: RecyclerView.ViewHolder): Boolean {
//重写item删除的动画
resetAnimation(holder)
mPendingRemovals.add(holder)
return true
}

override fun animateRemoveImpl(holder: RecyclerView.ViewHolder) {
//删除item时执行绕x轴0->90度的旋转
mRemoveAnimations.add(holder)
holder.itemView.animate().apply {
rotationX(90f)
duration = addDuration
interpolator = DecelerateInterpolator(3f)
setListener(object : AnimatorListenerAdapter() {

override fun onAnimationStart(animation: Animator?) {
dispatchRemoveStarting(holder)
}

override fun onAnimationCancel(animation: Animator?) {
ViewHelper.clear(holder.itemView)
}

override fun onAnimationEnd(animation: Animator) {
ViewHelper.clear(holder.itemView)
dispatchRemoveFinished(holder)
mRemoveAnimations.remove(holder)
dispatchFinishedWhenDone()
}
})
}.start()
}

}

demo地址:HoopAndroidDemos

四 RecyclerView列表动画的原理简析

«abstract»ItemAnimator«interface»ItemAnimatorListener«interface»ItemAnimatorFinishedListenerItemHolderInfo«abstract»SimpleItemAnimatorDefaultItemAnimator依赖依赖依赖继承继承

默认可预测动画执行的顺序

执行删除动画
移动动画
改变动画
item动画执行结束
执行延迟的item动画(下一帧)
默认效果为渐出
默认效果为平移
默认效果为渐出+渐入
添加动画(默认效果为渐入)
重置状态

列表动画是立即执行吗?
animateXXX()方法返回true时,延迟到在下一帧率执行.返回false时立即执行.
DefaultItemAnimator的动画都是在下一帧执行.

列表动画的实现流程

自定义立即执行的change动画:

RecyclerViewItenAnimatordispatchLayoutStep1()recordPreLayoutInformation()dispatchLayoutStep3()recordPostLayoutInformation()animateChange()RecyclerViewItenAnimator

默认延迟执行的add动画:

RecyclerViewItenAnimatordispatchLayoutStep3()animateMove()animateAdd()postAnimationRunner()animateMoveImplanimateAddImplRecyclerViewItenAnimator

0 个评论

要回复文章请先登录注册