由于recyclerView有动画的情况会创建双倍的viewHolder,没有了解的,可以看看我的上一篇 为什么选择放弃recyclerView ,可是如果没有动画,item的变化太突兀了,那么这个需求就这样出来了,如何不用recyclerView的默认动画实现动画
继承ItemAnimator或者SimpleItemAnimator
我仿佛在逗我笑,就算继承了,还是要调用setAnimator方法,这样还是会创建viewHolder
那么,自己给view设定动画是否可以呢?
我们知道,recyclerView是通过notifyItem来改变item的状态的,这就是传说中的观察者模式,那么我们没有
notifyItemChanged,就算设置了view的动画,但是item没有改变,高度也不会变化。
少年, 你还年轻,跟着page继续解读。那么从最基础的开始
recyclerView中用到animator的地方有什么需要关注的地方
- 首先设置:setItemAnimator,这个似乎没什么看的
public void setItemAnimator(ItemAnimator animator) {
if (mItemAnimator != null) {
mItemAnimator.endAnimations();
mItemAnimator.setListener(null);
}
mItemAnimator = animator;
if (mItemAnimator != null) {
mItemAnimator.setListener(mItemAnimatorListener);
}
}
嗯,重新设置的时候把listener给取消
2. 接下来我们看看这个animator在哪里初始化的
ItemAnimator mItemAnimator = new DefaultItemAnimator();
嗯,有一个默认的动画
3. 动画是在哪开启的呢?
private Runnable mItemAnimatorRunner = new Runnable() {
@Override
public void run() {
if (mItemAnimator != null) {
mItemAnimator.runPendingAnimations();
}
mPostedAnimatorRunner = false;
}
};
交给runnable去执行
private void postAnimationRunner() {
if (!mPostedAnimatorRunner && mIsAttached) {
ViewCompat.postOnAnimation(this, mItemAnimatorRunner);
mPostedAnimatorRunner = true;
}
}
private void animateAppearance(@NonNull ViewHolder itemHolder,
@Nullable ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo) {
itemHolder.setIsRecyclable(false);
if (mItemAnimator.animateAppearance(itemHolder, preLayoutInfo, postLayoutInfo)) {
postAnimationRunner();
}
}
private void animateDisappearance(@NonNull ViewHolder holder,
@NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo) {
addAnimatingView(holder);
holder.setIsRecyclable(false);
if (mItemAnimator.animateDisappearance(holder, preLayoutInfo, postLayoutInfo)) {
postAnimationRunner();
}
}
private void animateChange(@NonNull ViewHolder oldHolder, @NonNull ViewHolder newHolder,
@NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo,
boolean oldHolderDisappearing, boolean newHolderDisappearing) {
oldHolder.setIsRecyclable(false);
if (oldHolderDisappearing) {
addAnimatingView(oldHolder);
}
if (oldHolder != newHolder) {
if (newHolderDisappearing) {
addAnimatingView(newHolder);
}
oldHolder.mShadowedHolder = newHolder;
// old holder should disappear after animation ends
addAnimatingView(oldHolder);
mRecycler.unscrapView(oldHolder);
newHolder.setIsRecyclable(false);
newHolder.mShadowingHolder = oldHolder;
}
if (mItemAnimator.animateChange(oldHolder, newHolder, preInfo, postInfo)) {
postAnimationRunner();
}
}
有三个地方调用到了postAnimationRunner,要继续跟踪下去,略微复杂,page还无法做到三路线程同时运行,so,先缓一缓,我们先看看这个动画到底是做了什么的。
动画的内容
// 清除
private ArrayList<ViewHolder> mPendingRemovals = new ArrayList<>();
// 添加
private ArrayList<ViewHolder> mPendingAdditions = new ArrayList<>();
// 移动
private ArrayList<MoveInfo> mPendingMoves = new ArrayList<>();
// 改变
private ArrayList<ChangeInfo> mPendingChanges = new ArrayList<>();
private ArrayList<ArrayList<ViewHolder>> mAdditionsList = new ArrayList<>();
private ArrayList<ArrayList<MoveInfo>> mMovesList = new ArrayList<>();
private ArrayList<ArrayList<ChangeInfo>> mChangesList = new ArrayList<>();
private ArrayList<ViewHolder> mAddAnimations = new ArrayList<>();
private ArrayList<ViewHolder> mMoveAnimations = new ArrayList<>();
private ArrayList<ViewHolder> mRemoveAnimations = new ArrayList<>();
private ArrayList<ViewHolder> mChangeAnimations = new ArrayList<>();
private static class MoveInfo {
public ViewHolder holder;
public int fromX, fromY, toX, toY;
private MoveInfo(ViewHolder holder, int fromX, int fromY, int toX, int toY) {
this.holder = holder;
this.fromX = fromX;
this.fromY = fromY;
this.toX = toX;
this.toY = toY;
}
}
private static class ChangeInfo {
public ViewHolder oldHolder, newHolder;
public int fromX, fromY, toX, toY;
private ChangeInfo(ViewHolder oldHolder, ViewHolder newHolder) {
this.oldHolder = oldHolder;
this.newHolder = newHolder;
}
private ChangeInfo(ViewHolder oldHolder, ViewHolder newHolder,
int fromX, int fromY, int toX, int toY) {
this(oldHolder, newHolder);
this.fromX = fromX;
this.fromY = fromY;
this.toX = toX;
this.toY = toY;
}
}
从这里可以看到,这个animator对所有的viewHolder都有一个缓存
@Override
public void runPendingAnimations() {
boolean removalsPending = !mPendingRemovals.isEmpty();
boolean movesPending = !mPendingMoves.isEmpty();
boolean changesPending = !mPendingChanges.isEmpty();
boolean additionsPending = !mPendingAdditions.isEmpty();
if (!removalsPending && !movesPending && !additionsPending && !changesPending) {
// nothing to animate
return;
}
// First, remove stuff
for (ViewHolder holder : mPendingRemovals) {
animateRemoveImpl(holder);
}
mPendingRemovals.clear();
// Next, move stuff
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
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() {
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();
}
}
}
先移除,然后移动,再然后改变,最后添加。
在这些操作中,都可以看见循环的存在
我竟然在endAnimation中发现了一个todo
// TODO if some other animations are chained to end, how do we cancel them as well?
大致介绍完,我们具体来看看每个操作都做了啥
移除
// First, remove stuff
for (ViewHolder holder : mPendingRemovals) {
animateRemoveImpl(holder);
}
mPendingRemovals.clear();
private void animateRemoveImpl(final ViewHolder holder) {
final View view = holder.itemView;
final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view);
mRemoveAnimations.add(holder);
animation.setDuration(getRemoveDuration())
.alpha(0).setListener(new VpaListenerAdapter() {
@Override
public void onAnimationStart(View view) {
dispatchRemoveStarting(holder);
}
@Override
public void onAnimationEnd(View view) {
animation.setListener(null);
ViewCompat.setAlpha(view, 1);
dispatchRemoveFinished(holder);
mRemoveAnimations.remove(holder);
dispatchFinishedWhenDone();
}
}).start();
}
只是一个简单的alpha 变为0,在动画结束后又设置alpha为1
这不相当于没有改变吗?少年,你还是太简单了,如果这个view不在这上面了,那设置alpha=1又有什么关系?当然这只是我的一个推断而已,具体是否是这样,之后再看。
这个animatorRemove方法是在animateDisappearance(simpleItemAnimator类)中调用的,而这个方法又是在这调用的
private void animateDisappearance(@NonNull ViewHolder holder,
@NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo) {
addAnimatingView(holder);
holder.setIsRecyclable(false);
if (mItemAnimator.animateDisappearance(holder, preLayoutInfo, postLayoutInfo)) {
postAnimationRunner();
}
}
这个方法是返回true的,
@Override
public boolean animateRemove(final ViewHolder holder) {
resetAnimation(holder);
mPendingRemovals.add(holder);
return true;
}
也就是说,在动画开启前,先把viewHolder加入animator的缓存集合中,再开启动画
先到这里吧,国庆之后再继续