在上一篇中,我们分析了RecyclerView的绘制与复用。接下来我们继续分析RecyclerView的动画实现原理。上图展示的是一个Recyclerview的中,某个Item的删除,从动画执行preLayout阶段-> postLayout阶段 ->动画执行-> 动画结束后,item的回收 的整个流程。
在RecyclerView的,我们通过Adapter实现数据与View的绑定。当数据更改时,通过notifyItemXXX实现数据的更新。接下我们就首先分析一下notifyItemXXX究竟做了一件什么事情(以notifyItemRemoved为例)。
/**
* RecyclerView$Adapter类
*/
public abstract static class Adapter<VH extends ViewHolder> {
private final AdapterDataObservable mObservable = new AdapterDataObservable();
public final void notifyItemRemoved(int position) {
//观察者模式,将item移除事件通知类 “观察者”
//其中RecyclerViewDataObserver就是“观察者之一”
mObservable.notifyItemRangeRemoved(position, 1);
}
}
/**
* RecyclerView内置的观察者
*/
private class RecyclerViewDataObserver extends AdapterDataObserver {
//
@Override
public void onItemRangeRemoved(int positionStart, int itemCount) {
//通过mAdapterHelper,记录“删除操作”
if (mAdapterHelper.onItemRangeRemoved(positionStart, itemCount)) {
//调用成员方法
triggerUpdateProcessor();
}
}
//该方法中,请求requestLayout进行重新绘制
void triggerUpdateProcessor() {
...
mAdapterUpdateDuringMeasure = true;
requestLayout();
}
}
/**
* AdapterHelper.java
* 看一下AdapterHelper是如何记录 “删除操作”
*/
boolean onItemRangeRemoved(int positionStart, int itemCount) {
if (itemCount < 1) {
return false;
}
//将“删除操作”封装成对象,并保持到mPendingUpdates中
//在重新绘制的,PreLayout阶段进行使用。实现状态的同步。
mPendingUpdates.add(obtainUpdateOp(UpdateOp.REMOVE, positionStart, itemCount, null));
mExistingUpdateTypes |= UpdateOp.REMOVE;
return mPendingUpdates.size() == 1;
}
众所众知,ViewGroup可以通过LayoutTransition实现其内部childView的动画变化。在普通的ViewGroup,其内部的View的动画就是显示和隐藏。但是ReyclerView需要的动画有些不同,因为它有滚动的效果。比如:如下图所示,我们要删除Item C,Item G进入屏幕,会给人一种误解–在RecyclerView的尾部新增了Item G。
我们期望的效果是:
要想实现上图中的预期效果,我们需要记录屏幕中被影响变化的Item的 前后的位置变化和数据变化。而 PreLayout 和 PostLayout就是用来分别做这两件事情的。
在该阶段,RecyclerView要求LayoutManager使用附加信息布局之前的状态。比如:它会请求重新绘制items(此时C已经被删除了),LayoutManager运行它往常的布局步骤,但知道’C’将被删除,它布局items来填充’C’留下的空间。这个设计最酷的地方是,RecyclerView仍然表现得好像“C”仍然在Adapter中一样。
For example, when LayoutManager asks for the View for position 2, RecyclerView returns ‘C’ (getViewForPosition(2) == View(‘C’)) and if LayoutManager asks for position 4, RecyclerView returns the View for ‘E’ (although ‘D’ is the 4th item in the Adapter).
返回ItemView的LayoutParams有一个isItemRemove方法,LayoutManager可以使用该方法检查这是否是一个要删除的item。
‘C’ is not in the Adapter anymore. getViewForPosition(2) will return ‘D’ and getViewForPosition(4) will return ‘F’.
请记住,“C”的后台项已经从适配器中删除了,但是由于RecyclerView有它的视图表示,它可以表现得好像“C”还在那里一样。换句话说,RecyclerView为LayoutManager记账。
private void dispatchLayoutStep1() {
...
if (mState.mRunSimpleAnimations) {
// Step 0: 找出没有被删除的Item
int count = mChildHelper.getChildCount();
for (int i = 0; i < count; ++i) {
final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
if (holder.shouldIgnore() || (holder.isInvalid() && !mAdapter.hasStableIds())) {
continue;
}
//获取当前item的位置信息
final ItemAnimator.ItemHolderInfo animationInfo = mItemAnimator
.recordPreLayoutInformation(mState, holder,
ItemAnimator.buildAdapterChangeFlagsForAnimations(holder),
holder.getUnmodifiedPayloads());
//将item的位置信息放入到mViewInfoStore中进行缓存
mViewInfoStore.addToPreLayout(holder, animationInfo);
}
}
//processAdapterUpdatesAndSetAnimationFlags方法中设置的标记
if (mState.mRunPredictiveAnimations) {
// 保存原有位置的信息
// Save old positions so that LayoutManager can run its mapping logic、
saveOldPositions();
final boolean didStructureChange = mState.mStructureChanged;
mState.mStructureChanged = false;
//进行预绘制,将ViewHolder中的状态信息,同步到ViewInfoStroe中
//便于在PostLayout中,执行动画
mLayout.onLayoutChildren(mRecycler, mState);
mState.mStructureChanged = didStructureChange;
...
clearOldPositions();
} else {
//擦除位置信息
clearOldPositions();
}
...
}
private void dispatchLayoutStep3() {
//进入动画绘制阶段
mState.assertLayoutStep(State.STEP_ANIMATIONS);
startInterceptRequestLayout();
onEnterLayoutOrScroll();
mState.mLayoutStep = State.STEP_START;
if (mState.mRunSimpleAnimations) {
for (int i = mChildHelper.getChildCount() - 1; i >= 0; i--) {
...
//回去布局后的,holder的当前位置信息
final ItemAnimator.ItemHolderInfo animationInfo = mItemAnimator
.recordPostLayoutInformation(mState, holder);
ViewHolder oldChangeViewHolder = mViewInfoStore.getFromOldChangeHolders(key);
if (oldChangeViewHolder != null && !oldChangeViewHolder.shouldIgnore()) {
...
//将 postLayout阶段的 animationInfo 缓存到mViewInfoStore中
mViewInfoStore.addToPostLayout(holder, animationInfo);
}else{
...
//将 postLayout阶段的 animationInfo 缓存到mViewInfoStore中
mViewInfoStore.addToPostLayout(holder, animationInfo);
}
} else {
//将 postLayout阶段的 animationInfo 缓存到mViewInfoStore中
mViewInfoStore.addToPostLayout(holder, animationInfo);
}
}
//开始动画的执行
mViewInfoStore.process(mViewInfoProcessCallback);
}
...
}
接下来,我们接着上一节,继续分析动画。
// ViewInfoStore.java
void process(ProcessCallback callback) {
for (int index = mLayoutHolderMap.size() - 1; index >= 0; index--) {
final InfoRecord record = mLayoutHolderMap.removeAt(index);
if ((record.flags & FLAG_APPEAR_AND_DISAPPEAR) == FLAG_APPEAR_AND_DISAPPEAR) {
callback.unused(viewHolder);
} else if ((record.flags & FLAG_DISAPPEARED) != 0) {
...
//根据record.flags的不同状态,执行不同的动画
//record.flags状态的设置,源于 preLayout阶段的,LayoutManager.onLayoutChildren()方法
callback.processDisappeared(viewHolder, record.preInfo, record.postInfo);
} else{
...
}
//回收record info
InfoRecord.recycle(record);
}
}
接下来,看一下callback是怎么具体处理动画的。
/**
* 该成员变量 隶属于 RecyclerView
*/
private final ViewInfoStore.ProcessCallback mViewInfoProcessCallback =
new ViewInfoStore.ProcessCallback() {
@Override
public void processDisappeared(ViewHolder viewHolder, @NonNull ItemAnimator.ItemHolderInfo info,
@Nullable ItemAnimator.ItemHolderInfo postInfo) {
//将viewHolder从一级缓存中去除
mRecycler.unscrapView(viewHolder);
//调用执行动画的逻辑
animateDisappearance(viewHolder, info, postInfo);
}
};
void animateDisappearance(@NonNull ViewHolder holder,
@NonNull ItemAnimator.ItemHolderInfo preLayoutInfo, @Nullable ItemAnimator.ItemHolderInfo postLayoutInfo) {
//确保holder对应的View,已经被add到RecyclerView之上
//这是执行动画的基础之一。如果是removed的holder,则需要进行hide操作
addAnimatingView(holder);
holder.setIsRecyclable(false);
//到了这里,mItemAnimator,是不是就不陌生了
//这里是需要执行的缓存动画
if (mItemAnimator.animateDisappearance(holder, preLayoutInfo, postLayoutInfo)) {
//触发动画的执行
postAnimationRunner();
}
}
//注意,如果是“删除操作”,在动画执行完成的回掉里面,需要调用方法
//dispatchRemoveFinished(holder);进行holder的dettach和recycle
/**
* RecyclerView的成员变量
*/
private class ItemAnimatorRestoreListener implements ItemAnimator.ItemAnimatorListener {
ItemAnimatorRestoreListener() {
}
@Override
public void onAnimationFinished(ViewHolder item) {
item.setIsRecyclable(true);
//如果item需要回收
if (!item.shouldBeKeptAsChild()) {
//进行回收
if (!removeAnimatingView(item.itemView) && item.isTmpDetached()) {
//进行detach并且通知监听器
removeDetachedView(item.itemView, false);
}
}
}
}
上一篇RecyclerView源码分析二之绘制与复用
参考链接: