RecyclerView(6)-自定义ItemAnimator
自定义ItemAnimtor
不算太难,更何况还有一个官方的默认动画类DefaultItemAnimator
可以参考,相信很多同学都是会的,会自定义ItemAnimtor
的同学可以略过本篇,今天不扯淡了 直接进入主题!
目录如下:
1、 一些基础概念
2、 源码简单分析
· 2.1、以notifyItemInserted
为入口梳理RecyclerView中Animation流程
· 2.2、DefaultItemAnimtor
解读
3、 自定义RecyclerView.ItemAnimator
· 3.1 先生骨架
· 3.2 再长血肉
1、一些基础概念
官方有一个默认Item动画类DafaultItemAnimator
, 其中 DefaultItemAnimator
继承了SimpleItemAnimator
, 在继承了RecyclerView.ItemAnimator
1、SimpleItemAnimator
它是一个包装类,用来判断当前的ViewHolder到底是执行移动、移除、添加或者改变等行为。
2、DefaultItemAnimator
是执行具体动画类,它负责将viewHolder初始化、保存需要执行动画的ViveHolder以及动画信息、执行具体的动画。 我们自定义ItemAnimtor 也是仿照这个来的。
3、 其中DefaultItemAnimator
中animate + 动作
为名的的方法(比如animateAdd()
)做的事情有:计算动画信息,保存动画信息,初始化view的状态,且可以控制该VIewHolder
是否执行该次动画,如果返回值为false
那么那么不会执行该ViewHolder
的改次动画;
4、 DefaultItemAnimator
中animate + 动作 + Impl
为名的方法,做的动作是执行具体的动画动作。
5、runPendingAnimations
是最终执行具体动画的方法
2、 源码简单分析
2.1 以notifyItemInserted
为入口梳理RecyclerView中Animation流程
以 notifyitemInserted()
为入口分析 触发一个itemView的动画都经历了哪些类哪些方法;ps:其它 notifyitemitem...
等方法流程与之类似
1、触发notifyitemInserted
方法;
2、调用mObservable.notifyItemRangeInserted
被观察者方法;
3、遍历所注册的观察者RecyclerViewDataObserver
并调用其 onItemRangeInserted()
方法;
4 & 5、添加动画标识UpdateOp
并保存在队列中
7 & 8、执行 mUpdateChildViewsRunnable : Runnable
的 Run
方法,调用 consumePendingUpdateOperations方法
10&11&12&13&14&15&16 调用 这里做了一些列动画基础数据的配置与存储
17、调用 dispatchLayout方法,先会调用layoutManager的布局view 添加view;后在调用 dispatchLayoutStep3
方法
18、dispatchLayoutStep3()
该方法是这样解释的
* The final step of the layout where we save the information about views for animations,
* trigger animations and do any necessary cleanup.
最终在布局完成后,会触发item的动画
19-end 、调用ViewInfoStore.ProcessCallback.processPersistent
方法,其中内部调用到了 ItemAnimtor
的 animateChange
方法 ,在去调用 ItemAnimtor . runPendingAnimations
执行实现类设置的动画。
一个简单的 触发Item动画 时序过程就是如此了。 可以看到我们最终执行动画都是在 RecyclerView
中的dispatchLayoutStep3
执行的。执行具体动画在 ItemAnimtor
类的runPendingAnimations
方法
2.2 DefaultItemAnimtor
解读
这边着重得来分析一下 DefaultItemAnimtor
的runPendingAnimations
方法,因为该方法最终执行动画
@Override
public void runPendingAnimations() {
// 判断是否 remove、move、change、add 列表是否为空
boolean removalsPending = !mPendingRemovals.isEmpty();
boolean movesPending = !mPendingMoves.isEmpty();
boolean changesPending = !mPendingChanges.isEmpty();
boolean additionsPending = !mPendingAdditions.isEmpty();
if (!removalsPending && !movesPending && !additionsPending && !changesPending) {
return;
}
// First, remove stuff 先执行 remove列表的动画
for (ViewHolder holder : mPendingRemovals) {
animateRemoveImpl(holder);
}
// 清空存储
mPendingRemovals.clear();
// Next, move stuff 执行move动画
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();
}
}
// Next, change stuff, to run in parallel with move animations 执行vhange动画
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();
}
}
// Next, add stuff 执行添加动画
if (additionsPending) {
final ArrayList<ViewHolder> additions = new ArrayList<>();
additions.addAll(mPendingAdditions);
mAdditionsList.add(additions);
mPendingAdditions.clear();
Runnable adder = new Runnable() {
@Override
public void run() {
for (ViewHolder holder : additions) {
animateAddImpl(holder);
}
additions.clear();
mAdditionsList.remove(additions);
}
};
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();
}
}
}
通过以上信息可以了解到:runPendingAnimations
最终执行动画操作,
其中animate + 动作 + Impl 为名的方法,做具体的动画实现。
3、 自定义RecyclerView.ItemAnimator
3.1 先生骨架
我们要自定义一个BaseItemAnimator
,类图如下所示
其中BaseItemAnimator
仿照DefaultItemAnimator
的一个结构,唯一不同的是 执行具体动画代码被抽象为了方法。
FadeItemAnimator
、RotateItemAnimator
等继承BaseItemAnimator
是具体动画的实现类
这边仿照 DefaultItemAnimator
搭建据图框架Base框架
public class BaseItemAnimator extends SimpleItemAnimator {
//业务控制是否执行该viewHolder的动画 比如通讯录列表,判断只有联
//系人的ViewHolder执行动画,如果是分组头部ViewHolder则不执行动画
@Override
public boolean animateRemove(RecyclerView.ViewHolder holder) {
// 计算 holder的动画参数 如:x偏移量 y轴偏移量 透明度等等
// 保存 viewHolder 以及 动画参数
// 业务控制是否执行该viewHolder的动画
// 比如通讯录列表,判断只有联系人的ViewHolder执行动画,
// 如果是分组头部ViewHolder则不执行动画
return false;
}
@Override
public boolean animateAdd(RecyclerView.ViewHolder holder) {
// 计算 holder的动画参数 如:x偏移量 y轴偏移量 透明度等等
// 保存 viewHolder 以及 动画参数
// 业务控制是否执行该viewHolder的动画
return false;
}
//用于控制添加,移动更新时,其它Item的动画执行
@Override
public boolean animateMove(RecyclerView.ViewHolder holder, int fromX, int fromY, int toX, int toY) {
// 计算 holder的动画参数 如:x偏移量 y轴偏移量 透明度等等
// 保存 viewHolder 以及 动画参数
// 业务控制是否执行该viewHolder的动画
return false;
}
//Item更新回调
@Override
public boolean animateChange(RecyclerView.ViewHolder oldHolder, RecyclerView.ViewHolder newHolder, int fromLeft, int fromTop, int toLeft, int toTop) {
// 计算 holder的动画参数 如:x偏移量 y轴偏移量 透明度等等
// 保存 viewHolder 以及 动画参数
// 业务控制是否执行该viewHolder的动画
return false;
}
//真正控制执行动画的地方
@Override
public void runPendingAnimations() {
// 根据保存的 viewHolder 以及 animInfo 执行动画
}
//停止某个Item的动画
@Override
public void endAnimation(RecyclerView.ViewHolder item) {
}
//停止所有动画
@Override
public void endAnimations() {
}
@Override
public boolean isRunning() {
return false;
}
/**
* 执行移除动画
* @param holder 被移除的ViewHolder
* @param animator 被移动的ViewHolder对应动画对象
*/
public abstract void setRemoveAnimation(ViewHolder holder,ViewPropertyAnimatorCompat animator);
/**
* 执行移除动画结束,执行还原,因为该ViewHolder会被复用
* @param holder 被移除的ViewHolder
*/
public abstract void removeAnimationEnd(ViewHolder holder);
/**
* 执行添加动画初始化 这里设置透明为0添加时就会有渐变效果当然你可以在执行动画代码之前执行
* @param holder 添加的ViewHolder
*/
public abstract void addAnimationInit(ViewHolder holder);
/**
* 执行添加动画
* @param holder 添加的ViewHolder
* @param animator 添加的ViewHolder对应动画对象
*/
public abstract void setAddAnimation(ViewHolder holder,ViewPropertyAnimatorCompat animator);
/**
* 取消添加还原状态以复用
* @param holder 添加的ViewHolder
*/
public abstract void addAnimationCancel(ViewHolder holder);
/**
* 更新时旧的ViewHolder动画
* @param holder 旧的ViewHolder
* @param animator ViewHolder对应动画对象
*/
public abstract void setOldChangeAnimation(ViewHolder holder,ViewPropertyAnimatorCompat animator);
/**
* 更新时旧的ViewHolder动画结束,执行还原
* @param holder
*/
public abstract void oldChangeAnimationEnd(ViewHolder holder);
/**
* 更新时新的ViewHolder初始化
* @param holder 更新时新的ViewHolder
*/
public abstract void newChangeAnimationInit(ViewHolder holder);
/**
* 更新时新的ViewHolder动画
* @param holder 新的ViewHolder
* @param animator ViewHolder对应动画对象
*/
public abstract void setNewChangeAnimation(ViewHolder holder,ViewPropertyAnimatorCompat animator);
/**
* 更新时新的ViewHolder动画结束,执行还原
* @param holder
*/
public abstract void newChangeAnimationEnd(ViewHolder holder);
}
3.2、再长血肉
具体实现我这边只写一个 FadeItemAnimator
;
public class FadeItemAnimator extends BaseItemAnimator {
/**
* 执行移除动画
* @param holder 被移除的ViewHolder
* @param animator 被移动的ViewHolder对应动画对象
*/
@Override
public void setRemoveAnimation(RecyclerView.ViewHolder holder, ViewPropertyAnimatorCompat animator) {
animator.alpha(0);
}
/**
* 执行移除动画结束,执行还原,因为该ViewHolder会被复用
* @param view 被移除的ViewHolder
*/
@Override
public void removeAnimationEnd(RecyclerView.ViewHolder view) {
ViewCompat.setAlpha(view.itemView,1);
}
/**
* 执行添加动画初始化 这里设置透明为0添加时就会有渐变效果当然你可以在执行动画代码之前执行
* @param holder 添加的ViewHolder
*/
@Override
public void addAnimationInit(RecyclerView.ViewHolder holder) {
ViewCompat.setAlpha(holder.itemView, 0);
}
/**
* 执行添加动画
* @param holder 添加的ViewHolder
* @param animator 添加的ViewHolder对应动画对象
*/
@Override
public void setAddAnimation(RecyclerView.ViewHolder holder,ViewPropertyAnimatorCompat animator) {
animator.alpha(1);
}
/**
* 取消添加还原状态以复用
* @param holder 添加的ViewHolder
*/
@Override
public void addAnimationCancel(RecyclerView.ViewHolder holder) {
ViewCompat.setAlpha(holder.itemView, 1);
}
/**
* 更新时旧的ViewHolder动画
* @param holder 旧的ViewHolder
* @param animator ViewHolder对应动画对象
*/
@Override
public void setOldChangeAnimation(RecyclerView.ViewHolder holder, ViewPropertyAnimatorCompat animator) {
animator.alpha(0);
}
/**
* 更新时旧的ViewHolder动画结束,执行还原
* @param holder
*/
@Override
public void oldChangeAnimationEnd(RecyclerView.ViewHolder holder) {
ViewCompat.setAlpha(holder.itemView,1);
}
/**
* 更新时新的ViewHolder初始化
* @param holder 更新时新的ViewHolder
*/
@Override
public void newChangeAnimationInit(RecyclerView.ViewHolder holder) {
ViewCompat.setAlpha(holder.itemView,0);
}
/**
* 更新时新的ViewHolder动画
* @param holder 新的ViewHolder
* @param animator ViewHolder对应动画对象
*/
@Override
public void setNewChangeAnimation(RecyclerView.ViewHolder holder, ViewPropertyAnimatorCompat animator) {
animator.alpha(1);
}
/**
* 更新时新的ViewHolder动画结束,执行还原
* @param holder
*/
@Override
public void newChangeAnimationEnd(RecyclerView.ViewHolder holder) {
ViewCompat.setAlpha(holder.itemView,1);
}
}
其他动画实现具体查看源码,也可以参考DefaultItemAnimtor
实现动画细节,当然在下面有对DefaultItemAnimtor
做一个源码分析。
源码戳我!
· RecyclerView(1)- Decoration源码解析
· RecyclerView(2)- 自定义Decoration打造时光轴效果
· RecyclerView(3)- LayoutMagager源码解析,LinearLayoutManager
· RecyclerView(4)- 核心、Recycler复用机制
· RecyclerView(5)- 自定义LayoutManager(布局、复用)
· RecyclerView(6)- 自定义ItemAnimator
· RecyclerView(7)- ItemTouchHelper
· RecyclerView(8)- MultiTypeAdapter文章、MultiTypeAdapter Github地址
希望我的文章不会误导在观看的你,如果有异议的地方欢迎讨论和指正。
如果能给观看的你带来收获,那就是最好不过了。