RecyclerView动画源码浅析
adapter.notifyItemRemoved(1)会回调到 RecyclerViewDataObserver:
adapter.notifyItemRemoved RecyclerViewDataObserver
onItemRangeRemoved
triggerUpdateProcessor
mAdapterUpdateDuringMeasure
consumePendingUpdateOperations
dispatchLayout
dispatchLayoutStep1
processAdapterUpdatesAndSetAnimationFlags
preProcess
applyRemove
postponeAndUpdateViewHolders
offsetPositionsForRemovingLaidOutOrNewView
offsetPositionRecordsForRemove
flagRemovedAndOffsetPosition
setFrom
ItemHolderInfo ViewHolder mViewInfoStore mLayoutHolderMap 保存动画前View的现场
if (POST_UPDATES_ON_ANIMATION && mHasFixedSize && mIsAttached) {
ViewCompat.postOnAnimation(RecyclerView.this, mUpdateChildViewsRunnable);
}
final Runnable mUpdateChildViewsRunnable = new Runnable() {
@Override
public void run() {
consumePendingUpdateOperations();
dispatchLayout();
dispatchLayoutStep1();
@Override
public void onItemRangeRemoved(int positionStart, int itemCount) {
assertNotInLayoutOrScroll(null);
if (mAdapterHelper.onItemRangeRemoved(positionStart, itemCount)) {
triggerUpdateProcessor();
}
}
mPendingUpdates.add(obtainUpdateOp(UpdateOp.REMOVE, positionStart, itemCount, null));
final ArrayList<UpdateOp> mPendingUpdates = new ArrayList<UpdateOp>();
triggerUpdateProcessor()调用了requestLayout, 即触发了RecyclerView的重新布局。
void triggerUpdateProcessor() {
if (POST_UPDATES_ON_ANIMATION && mHasFixedSize && mIsAttached) {
ViewCompat.postOnAnimation(RecyclerView.this, mUpdateChildViewsRunnable);
} else {
mAdapterUpdateDuringMeasure = true;
requestLayout();
}
}
void dispatchLayout() {
dispatchLayoutStep1();
processAdapterUpdatesAndSetAnimationFlags();
mAdapterHelper.preProcess();
case UpdateOp.REMOVE:
applyRemove(op);
break;
postponeAndUpdateViewHolders(op);
case UpdateOp.REMOVE:
mCallback.offsetPositionsForRemovingLaidOutOrNewView(op.positionStart,
op.itemCount);
break;
void offsetPositionRecordsForRemove(int positionStart, int itemCount,
boolean applyToPreLayout) {
holder.flagRemovedAndOffsetPosition(positionStart - 1, -itemCount,
applyToPreLayout);
mState.mStructureChanged = true;
flagRemovedAndOffsetPosition
offsetPositionRecordsForRemove方法:主要是把当前显示在界面上的ViewHolder的位置做对应的改变,即如果item位于删除的item之后,那么它的位置应该减一,比如原来的位置是3现在变成了2。
RecyclerView
@Override
public void offsetPositionsForRemovingLaidOutOrNewView(
int positionStart, int itemCount) {
offsetPositionRecordsForRemove(positionStart, itemCount, false);
mItemsAddedOrRemoved = true;
}
final ItemHolderInfo animationInfo
@NonNull
public ItemHolderInfo setFrom(@NonNull RecyclerView.ViewHolder holder,
@AdapterChanges int flags) {
final View view = holder.itemView;
this.left = view.getLeft();
this.top = view.getTop();
this.right = view.getRight();
this.bottom = view.getBottom();
return this;
}
mViewInfoStore.addToPreLayout(holder, animationInfo);
static Pools.Pool<InfoRecord> sPool = new Pools.SimplePool<>(20);
即把holder 和 info保存到 mLayoutHolderMap 中。可以理解为它是用来保存动画执行前当前界面ViewHolder的信息一个集合。
@VisibleForTesting
final ArrayMap<RecyclerView.ViewHolder, InfoRecord> mLayoutHolderMap = new ArrayMap<>();
其实这些操作可以简单的理解为保存动画前View的现场
dispatchLayoutStep2
// Step 2: Run layout
mState.mInPreLayout = false;
mLayout.onLayoutChildren(mRecycler, mState);
fill(recycler, mLayoutState, state, false);
layoutChunk(recycler, state, layoutState, layoutChunkResult);
View view = layoutState.next(recycler);
final View view = recycler.getViewForPosition(mCurrentPosition);
return tryGetViewHolderForPositionByDeadline(position, dryRun, FOREVER_NS).itemView;
dispatchLayoutStep3(执行删除动画)
@VisibleForTesting
final LongSparseArray<RecyclerView.ViewHolder> mOldChangedHolders = new LongSparseArray<>()
// Step 4: Process view info lists and trigger animations
mViewInfoStore.process(mViewInfoProcessCallback);
private final ViewInfoStore.ProcessCallback mViewInfoProcessCallback =
public void processDisappeared(ViewHolder viewHolder, @NonNull ItemHolderInfo info,
@Nullable ItemHolderInfo postInfo) {
mRecycler.unscrapView(viewHolder);
animateDisappearance(viewHolder, info, postInfo);
callback.processDisappeared(viewHolder, record.preInfo, record.postInfo);
}
animateDisappearance(viewHolder, info, postInfo);
addAnimatingView(holder);
// re-attach
mChildHelper.attachViewToParent(view, -1, view.getLayoutParams(), true);
if (mItemAnimator.animateDisappearance(holder, preLayoutInfo, postLayoutInfo)) {
return animateMove(viewHolder, oldLeft, oldTop, newLeft, newTop);
if (deltaX != 0) {
view.setTranslationX(-deltaX);
}
if (deltaY != 0) {
view.setTranslationY(-deltaY);
}
mPendingMoves.add(new MoveInfo(holder, fromX, fromY, toX, toY));
private ArrayList<MoveInfo> mPendingMoves = new ArrayList<>();
将未被删除的Item的移动动画放入到mPendingMoves待执行队列
postAnimationRunner();
rivate Runnable mItemAnimatorRunner = new Runnable() {
@Override
mItemAnimator.runPendingAnimations();
animateMoveImpl(moveInfo.holder, moveInfo.fromX, moveInfo.fromY,
moveInfo.toX, moveInfo.toY);
animateRemoveImpl 把这个被Remove的Item做一个透明度由(1~0)的动画
animateMoveImpl把它们的TranslationX和TranslationY移动到0的位置。
来源:oschina
链接:https://my.oschina.net/u/4351246/blog/4375191