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;
}
添加/删除/改变都是渐入渐出动画:
使用预测性动画
//RecyclerView.LayoutManager.java
public boolean supportsPredictiveItemAnimations() {
return true;
}
二 配合LayoutAnimation使用
RecyclerView
和普通的ViewGroup
一样支持LayoutAnimation
.使用起来比较简单,设置RecyclerView
的 animateLayoutChanges
和layoutAnimation
属性即可.
案例
图片出处:Layout animations on RecyclerView
图片出处:RecyclerView 与 LayoutAnimation 实现的进入动画(一 ): List
三 自定义RecyclerView列表动画
案例学习:自定义change动画
效果:
代码实现:
1.新建MyChangeAnimator
类继承DefaultItemAnimator
private class MyChangeAnimator extends DefaultItemAnimator {
}
2.重写DefaultItemAnimator
的canReuseUpdatedViewHolder()
方法.
@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.重写DefaultItemAnimator
的obtainHolderInfo()
方法,新建一个ColorTextInfo
对象返回.
@Override
public ItemHolderInfo obtainHolderInfo() {
return new ColorTextInfo();
}
- 重写
DefaultItemAnimator
的recordPreLayoutInformation()
和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.重写DefaultItemAnimator
的animateChange()
方法,执行自定义的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.设置RecyclerView
的ItemAnimator
为自定义的MyChangeAnimator
mRecyclerView.setItemAnimator(mChangeAnimator);
这是简化后的代码.官方完整的demo里还包括了边界情况的处理,处理了上一次change动画没执行完时触发新的change动画的情况.
代码地址:github.com/android/vie…
自定义add和del动画
目标效果:
代码实现:
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列表动画的原理简析
默认可预测动画执行的顺序
列表动画是立即执行吗?
animateXXX()方法返回true时,延迟到在下一帧率执行.返回false时立即执行.
DefaultItemAnimator的动画都是在下一帧执行.
列表动画的实现流程
自定义立即执行的change动画:
默认延迟执行的add动画: