抽丝剥茧 RecyclerView
系列文章的目的在于帮助Android开发者提升对RecyclerView的认知,本文是整个系列的第三篇。缓存
在前面的系列文章中,咱们从源码的角度分别研究了:bash
RecyclerView
(总体结构)Recycler
LayoutManger
ItemDecoration
(略微了解)纵观RecyclerView
,彷佛还剩下ItemAnimator
和Adapter
,那么本文做为**抽丝剥茧RecyclerView
**系列的最后一篇,天然要将剩下的部分所有分析完毕(文末有往期文章的连接)。app
我将Adapter
称为RecyclerView
中的魔法师,为何叫它魔法师呢?由于它将数据变成了具体的视图,不过这也是咱们平时谈论颇多的适配器模式。ide
Adapter
的主要功能是数据转子视图和数据管理及通知,因此在了解源码以前,咱们还需了解Adpater
的相关类:函数
名称 | 做用 |
---|---|
AdapterDataObservable |
数据发生变化的时候实际处理的类 |
ViewHolder |
存入子视图 和当前的位置信息,你们应该都很熟悉了~ |
在以前的文章《抽丝剥茧RecyclerView - 化整为零》咱们介绍Recycler
的时候,已经了解到在Recycler
若是没有缓存ViewHolder
,会调用Adapter#onCreateViewHolder
建立一个ViewHolder
,咱们日常在该方法的实现中,一般会:源码分析
View root = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.xxx,viewGroup,false);
return new ViewHolder(root);
复制代码
以这样的方式建立子视图
,建立完的子视图
会交给ViewHolder
管理,存储在ViewHolder中的itemView
,接着Recycler
会调用Adapter#onBindViewHolder
实现将数据展现在控件中,不过这两个方法都是由控件的使用者实现。布局
每次数据发生变化的时候,咱们都须要调用Adapter#notifyxxx
通知RecyclerView
数据集发生了变化。此次咱们以删除为例来分析源码。post
设置适配器的代码是RecyclerView#setAdapter
:动画
public void setAdapter(Adapter adapter) {
// ...
// 重点方法
setAdapterInternal(adapter, false, true);
// ...
}
private void setAdapterInternal(Adapter adapter, Boolean compatibleWithPrevious,
Boolean removeAndRecycleViews) {
if (mAdapter != null) {
// 旧的适配器解除注册
mAdapter.unregisterAdapterDataObserver(mObserver);
mAdapter.onDetachedFromRecyclerView(this);
}
// ...
final Adapter oldAdapter = mAdapter;
// 对新的适配器检测数据监听
mAdapter = adapter;
if (adapter != null) {
adapter.registerAdapterDataObserver(mObserver);
adapter.onAttachedToRecyclerView(this);
}
// ...
}
复制代码
该代码主要做用有两点:ui
数据变化通知对象是这个mObserver
,来看看这个mObserver
是什么:
private final RecyclerViewDataObserver mObserver = new RecyclerViewDataObserver();
// 数据发生变化的回调接口
// 通知到RecyclerView中
// RecyclerViewDataObserver继承自AdapterDataObserver
public abstract static class AdapterDataObserver {
public void onChanged() {
// Do nothing
}
public void onItemRangeChanged(int positionStart, int itemCount) {
// do nothing
}
public void onItemRangeChanged(int positionStart, int itemCount, @Nullable Object payload) {
// fallback to onItemRangeChanged(positionStart, itemCount) if app
// does not override this method.
onItemRangeChanged(positionStart, itemCount);
}
public void onItemRangeInserted(int positionStart, int itemCount) {
// do nothing
}
public void onItemRangeRemoved(int positionStart, int itemCount) {
// do nothing
}
public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
// do nothing
}
}
复制代码
而这个RecyclerViewDataObserver
则是继承自AdapterDataObserver
抽象类,具体的实现细节咱们后面再讨论。
使用场景是这样的:
btnDeleteOne.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
List<String> strings = mAdapter.getValues();
if(strings.size() == 0)
return;
// 移除第一个数据
strings.remove(0);
// 适配器通知删除
mAdapter.notifyItemRemoved(0);
}
复制代码
这里有必要说明一下:Adapter
和RecyclerViewDataObserver
都是RecyclerView
的内部类,因此它们能够直接使用RecyclerView
内部的资源。
当RecyclerView
中的数据删除的时候,咱们调用了Adapter#notifyRemoved
方法:
public final void notifyItemRemoved(int position) {
mObservable.notifyItemRangeRemoved(position, 1);
}
复制代码
发现删除的处理交给了上面介绍的mObservable
,咱们来看一下RecyclerViewDataObserver#notifyItemRemoved
具体实现:
private class RecyclerViewDataObserver extends AdapterDataObserver {
//... 省略一些方法
@Override
public void onItemRangeRemoved(int positionStart, int itemCount) {
assertNotInLayoutOrScroll(null);
if (mAdapterHelper.onItemRangeRemoved(positionStart, itemCount)) {
triggerUpdateProcessor();
}
}
// 刷新界面或者直接进行动画
// 删除这里是调用的刷新界面
void triggerUpdateProcessor() {
if (POST_UPDATES_ON_ANIMATION && mHasFixedSize && mIsAttached) {
ViewCompat.postOnAnimation(RecyclerView.this, mUpdateChildViewsRunnable);
} else {
mAdapterUpdateDuringMeasure = true;
requestLayout();
}
}
}
复制代码
在RecyclerViewDataObserver
的通知删除方法中,它又把删除的处理交给了AdapterHelper
,调用了AdapterHelper#onItemRangeRemoved
:
/**
* @return True if updates should be processed.
*/
Boolean onItemRangeRemoved(int positionStart, int itemCount) {
if (itemCount < 1) {
return false;
}
// mPendingUpdates是List<UpdateOp>
// 这里是将一个删除的UpdateOp加入mPendingUpdates中
mPendingUpdates.add(obtainUpdateOp(UpdateOp.REMOVE, positionStart, itemCount, null));
mExistingUpdateTypes |= UpdateOp.REMOVE;
return mPendingUpdates.size() == 1;
}
复制代码
AdapterHelper
对本身比较有信心,没有交给别人处理,他在本身的mPendingUpdates
中加入一个删除标记的UpdateOp
,这个mPendingUpdates
有什么做用呢?咱们一样在使用的时候介绍。回到RecyclerViewDataObserver
中的RecyclerViewDataObserver#notifyItemRemoved
,调用完AdapterHelper#onItemRangeRemoved
以后,它立马又调用了requestLayout
进行界面刷新。
界面绘制一直是咱们以前博客的重点讨论对象,本章咱们就数据通知再看一下关于数据通知的细节。
在RecyclerView#dispatchLayoutStep1
方法中,RecyclerView
会调用RecyclerView#processAdapterUpdatesAndSetAnimationFlags
处理Adapter
中的更新和为动画设置标记,这里咱们只看适配器数据更新相关:
private void processAdapterUpdatesAndSetAnimationFlags() {
//...
// simple animations are a subset of advanced animations (which will cause a
// pre-layout step)
// If layout supports predictive animations, pre-process to decide if we want to run them
if (predictiveItemAnimationsEnabled()) {
mAdapterHelper.preProcess();
} else {
mAdapterHelper.consumeUpdatesInOnePass();
}
// ...
}
private Boolean predictiveItemAnimationsEnabled() {
// RecyclerView设置了默认的mItemAnimator,
// 以及LinearLayout的supportsPredictiveItemAnimations()为true
// 该方法返回为true
return (mItemAnimator != null && mLayout.supportsPredictiveItemAnimations());
}
复制代码
因为RecyclerView#predictiveItemAnimationsEnabled
一般会返回true
,那咱们跳到AdapterHelper
,查看AdapterHelper#preProcess
方法:
void preProcess() {
// ...
final int count = mPendingUpdates.size();
for (int i = 0; i < count; i++) {
UpdateOp op = mPendingUpdates.get(i);
switch (op.cmd) {
// ... 添加省略
case UpdateOp.REMOVE:
applyRemove(op);
break;
// 更新、移动标签省略
}
}
mPendingUpdates.clear();
}
复制代码
mPendingUpdates
是一个ArrayList<UpdateOp>
,上述方法就是消费咱们在以前添加进mPendingUpdates
的删除UpdateOp
,在处理删除属性的UpdateOp
的AdapterHelper#applyRemove
方法中又调用AdapterHelper#postponeAndUpdateViewHolders
:
private void postponeAndUpdateViewHolders(UpdateOp op) {
mPostponedList.add(op);
switch (op.cmd) {
// ... 省略添加、更新、移动
case UpdateOp.REMOVE:
mCallback.offsetPositionsForRemovingLaidOutOrNewView(op.positionStart,op.itemCount);
break;
default:
throw new IllegalArgumentException("Unknown update op type for " + op);
}
}
复制代码
真实的处理交给了AdapterHelper
中的mCallback
,而mCallback
的实现一样也在RecyclerView
,那咱们直接查看mCallback
的具体实现:
void initAdapterManager() {
mAdapterHelper = new AdapterHelper(new AdapterHelper.Callback() {
// ...省略其余方法
// 仅仅展现删除的方法
@Override
public void offsetPositionsForRemovingLaidOutOrNewView(
int positionStart, int itemCount) {
offsetPositionRecordsForRemove(positionStart, itemCount, false);
mItemsAddedOrRemoved = true;
}
//... 省略其余方法
});
}
void offsetPositionRecordsForRemove(int positionStart, int itemCount,
Boolean applyToPreLayout) {
final int positionEnd = positionStart + itemCount;
final int childCount = mChildHelper.getUnfilteredChildCount();
for (int i = 0; i < childCount; i++) {
final ViewHolder holder = getChildViewHolderint(mChildHelper.getUnfilteredChildAt(i));
if (holder != null && !holder.shouldIgnore()) {
if (holder.mPosition >= positionEnd) {
// 更新未删除的ViewHOlder的的位置信息
holder.offsetPosition(-itemCount, applyToPreLayout);
mState.mStructureChanged = true;
} else if (holder.mPosition >= positionStart) {
// 跟新要删除逇ViewHolder的位置信息
holder.flagRemovedAndOffsetPosition(positionStart - 1, -itemCount,
applyToPreLayout);
mState.mStructureChanged = true;
}
}
}
mRecycler.offsetPositionRecordsForRemove(positionStart, itemCount, applyToPreLayout);
requestLayout();
}
复制代码
上述代码的做用主要有两点:
ViewHolder
加上删除的flag
,更新ViewHolder
的位置ViewHolder
则更新位置在数据删除之后,Adapter
的做用就是为这些变化的ViewHolder
添加删除标签和更新位置信息,后续的处理就交给了LayoutManager
和ItemAnimator
,咱们在下面的动画中分析~
好的动画会让界面的交互很天然,RecyclerView
做为一款强大的UI控件,天然也是支持动画的,没错,RecyclerView
子视图动画是由ItemAnimator
实现的。
上文中的Gif不适合讲解,因而我换了一张聊天图,一样要删除第一条信息:
Adapter
的结果是它更新了
ViewHolder的
一些
flag
,那么这些有了
flag
的
ViewHolder
是如何处理的呢?
在此以前,简单了解一下动画相关类ViewInfoStore
:
预布局是什么呢?简单来讲,RecyclerView
进行真实的布局以前,提早进行一次布局,也就是说,LayoutManager#onLayoutChildren
方法会执行两次,那么为何会执行两次呢?咱们慢慢分析。
预布局是一个很重要得过程,当有简单的子视图动画发生的时候,它就会被触发,这一点咱们得回顾一下RecyclerView#dispatchLayoutStep1
方法,直接进入其中的RecyclerView#processAdapterUpdatesAndSetAnimationFlags
方法:
private void processAdapterUpdatesAndSetAnimationFlags() {
// simple animations are a subset of advanced animations (which will cause a
// pre-layout step)
// If layout supports predictive animations, pre-process to decide if we want to run them
if (predictiveItemAnimationsEnabled()) {
mAdapterHelper.preProcess();
} else {
mAdapterHelper.consumeUpdatesInOnePass();
}
Boolean animationTypeSupported = mItemsAddedOrRemoved || mItemsChanged;
// mFirstLayoutComplete会在第一次布局完成之后设置为true
mState.mRunSimpleAnimations = mFirstLayoutComplete
&& mItemAnimator != null
&& (mDataSetHasChangedAfterLayout
|| animationTypeSupported
|| mLayout.mRequestedSimpleAnimations)
&& (!mDataSetHasChangedAfterLayout
|| mAdapter.hasStableIds());
mState.mRunPredictiveAnimations = mState.mRunSimpleAnimations
&& animationTypeSupported
&& !mDataSetHasChangedAfterLayout
&& predictiveItemAnimationsEnabled();
}
复制代码
从上面的代码中,咱们能够看出:
RecyclerView
第一次布局完成之后才有资格触发动画,mFirstLayoutComplete
是在第一次布局完成之后设置为true
mState.mRunSimpleAnimations
为true
是mState.mRunPredictiveAnimations
为true
的充要条件,mState.mRunPredictiveAnimations
这个属性很重要,由它决定是否进行预布局从新回到RecyclerView#dispatchLayoutStep1
方法:
private void dispatchLayoutStep1() {
// ...
mViewInfoStore.clear();
processAdapterUpdatesAndSetAnimationFlags();
// 重置一些状态
// ... 省略
mItemsAddedOrRemoved = mItemsChanged = false;
// 是否预布局取决于mState.mRunPredictiveAnimations
mState.mInPreLayout = mState.mRunPredictiveAnimations;
if (mState.mRunSimpleAnimations) {
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;
}
// 记录当前的位置信息 Left、Right、Top、Bottom等
final ItemHolderInfo animationInfo = mItemAnimator
.recordPreLayoutInformation(mState, holder,
ItemAnimator.buildAdapterChangeFlagsForAnimations(holder),
holder.getUnmodifiedPayloads());
mViewInfoStore.addToPreLayout(holder, animationInfo);
// ... 省略
}
}
if (mState.mRunPredictiveAnimations) {
// Step 1: run prelayout: This will use the old positions of items. The layout manager
// is expected to layout everything, even removed items (though not to add removed
// items back to the container). This gives the pre-layout position of APPEARING views
// which come into existence as part of the real layout.
// 大体就是layoutManager会layout每个子视图,包括后面加入的子视图和删除的子视图,这样之后,layoutManager就很清楚
// 要执行哪些动画了
saveOldPositions();
final Boolean didStructureChange = mState.mStructureChanged;
mState.mStructureChanged = false;
// temporarily disable flag because we are asking for previous layout
mLayout.onLayoutChildren(mRecycler, mState);
mState.mStructureChanged = didStructureChange;
for (int i = 0; i < mChildHelper.getChildCount(); ++i) {
final View child = mChildHelper.getChildAt(i);
final ViewHolder viewHolder = getChildViewHolderint(child);
// ...
if (!mViewInfoStore.isInPreLayout(viewHolder)) {
// 对于新出来的ViewHolder添加标签
// ... 省略一些方法
mViewInfoStore.addToAppearedInPreLayoutHolders(viewHolder, animationInfo);
}
}

clearOldPositions();
} else {
clearOldPositions();
}
// ...
}
复制代码
除了上面直接进入的方法,还有两个if
语句。
这个内容很简单,对预布局前存在的ViewHolder
的的位置信息进行记录。
第二个if语句的内容就复杂多了,首先会进行预布局过程,该过程第一次调用了LayoutManager#onLayoutChildren
,关于布局的具体过程,这里我就不讲解了,想要了解的同窗能够翻阅我以前的文章:《抽丝剥茧RecyclerView - LayoutManager》。
须要指出的是,在添加子视图中,调用了LayoutManager#addViewInt
方法:
private void addViewint(View child, int index, Boolean disappearing) {
// ...
if (disappearing || holder.isRemoved()) {
// these views will be hidden at the end of the layout pass.
mRecyclerView.mViewInfoStore.addToDisappearedInLayout(holder);
} else {
// This may look like unnecessary but may happen if layout manager supports
// predictive layouts and adapter removed then re-added the same item.
// In this case, added version will be visible in the post layout (because add is
// deferred) but RV will still bind it to the same View.
// So if a View re-appears in post layout pass, remove it from disappearing list.
mRecyclerView.mViewInfoStore.removeFromDisappearedInLayout(holder);
}
// ...
}
复制代码
该方法的目的是若是是被删除的ViewHolder
,它会为ViewInfoStore
中ViewHolder
对应的记录InfoRecord
添加已删除的标记,在真实的布局(非预布局)中,被删除的ViewHolder
是不会被使用的,因此说,只有预布局才会记录删除动画。
预布局完成,界面的样子:
能够看到,第一次布局完了之后,须要删除的ViewHolder
和自动填充的ViewHolder
都被加入了RecyclerView
,不过,RecyclerView#DispatchLayoutStep1
还没结束,它会调用ViewInfoStore
会给新加入的ViewHolder
添加对应的InfoRecord
。
完成这个之后,RecyclerView
对于要处理哪些动画就了如指掌了,这个也是预布局的意义。
一样不讲具体的代码,第二次布局完成之后,界面变成了:
Recycler
的源码了🧐:
ViewHolder getScrapOrHiddenOrCachedHolderForPosition(int position, Boolean dryRun) {
final int scrapCount = mAttachedScrap.size();
// Try first for an exact, non-invalid match from scrap.
for (int i = 0; i < scrapCount; i++) {
final ViewHolder holder = mAttachedScrap.get(i);
if (... && (mState.mInPreLayout || !holder.isRemoved())) {
// 在第一级缓存mAttachedScrap中,若是是删除的ViewHolder
// 预布局是可使用的,真实布局不可使用
holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
return holder;
}
}
//...
return null;
}
复制代码
虽然解决了当前的疑问,你可能还会有另一个疑问,**没有了被删除的子视图,删除动画还怎么执行呢?**咱们仍是先看看接下来的过程吧。
以前咱们记录了那么多ViewHolder
中子视图的信息,如今到了使用的时候了:
private void dispatchLayoutStep3() {
// ...
if (mState.mRunSimpleAnimations) {
// Step 3: Find out where things are now, and process change animations.
// 找到当前的ViewHolder,执行须要执行的动画
for (int i = mChildHelper.getChildCount() - 1; i >= 0; i--) {
ViewHolder holder = getChildViewHolderint(mChildHelper.getChildAt(i));
long key = getChangedHolderKey(holder);
final ItemHolderInfo animationInfo = mItemAnimator
.recordPostLayoutInformation(mState, holder);
ViewHolder oldChangeViewHolder = mViewInfoStore.getFromOldChangeHolders(key);
if (oldChangeViewHolder != null && !oldChangeViewHolder.shouldIgnore()) {
// ...
if (oldDisappearing && oldChangeViewHolder == holder) {
// run disappear animation instead of change
mViewInfoStore.addToPostLayout(holder, animationInfo);
} else {
// ...
mViewInfoStore.addToPostLayout(holder, animationInfo);
ItemHolderInfo postInfo = mViewInfoStore.popFromPostLayout(holder);
// ...
}
} else {
mViewInfoStore.addToPostLayout(holder, animationInfo);
}
}
// Step 4: Process view info lists and trigger animations
// 执行动画
mViewInfoStore.process(mViewInfoProcessCallback);
}
// 重置一些跟动画有关的类
// ...
mState.mRunSimpleAnimations = false;
mState.mRunPredictiveAnimations = false;
// ...
mViewInfoStore.clear();
}
复制代码
这个函数的上半部分主要的目的是为了给ViewInfoStore
里的ViewHolder
相关的InfoRecord
添加Post
标签,下半部分mViewInfoStore.process(mViewInfoProcessCallback)
则是咱们的核心功能 - 动画执行,咱们重点看一下这个方法:
void process(ProcessCallback callback) {
for (int index = mLayoutHolderMap.size() - 1; index >= 0; index--) {
final ViewHolder viewHolder = mLayoutHolderMap.keyAt(index);
final InfoRecord record = mLayoutHolderMap.removeAt(index);
// 根据不一样的Flag执行不一样的动画
if ((record.flags & FLAG_APPEAR_AND_DISAPPEAR) == FLAG_APPEAR_AND_DISAPPEAR) {
callback.unused(viewHolder);
} else if ((record.flags & FLAG_DISAPPEARED) != 0) {
// Set as "disappeared" by the LayoutManager (addDisappearingView)
if (record.preInfo == null) {
callback.unused(viewHolder);
} else {
callback.processDisappeared(viewHolder, record.preInfo, record.postInfo);
}
} else if ((record.flags & FLAG_APPEAR_PRE_AND_POST) == FLAG_APPEAR_PRE_AND_POST) {
// Appeared in the layout but not in the adapter (e.g. entered the viewport)
callback.processAppeared(viewHolder, record.preInfo, record.postInfo);
} else if ((record.flags & FLAG_PRE_AND_POST) == FLAG_PRE_AND_POST) {
// Persistent in both passes. Animate persistence
callback.processPersistent(viewHolder, record.preInfo, record.postInfo);
} else if ((record.flags & FLAG_PRE) != 0) {
// Was in pre-layout, never been added to post layout
callback.processDisappeared(viewHolder, record.preInfo, null);
} else if ((record.flags & FLAG_POST) != 0) {
// Was not in pre-layout, been added to post layout
callback.processAppeared(viewHolder, record.preInfo, record.postInfo);
} else if ((record.flags & FLAG_APPEAR) != 0) {
// Scrap view. RecyclerView will handle removing/recycling this.
} else if (DEBUG) {
throw new IllegalStateException("record without any reasonable flag combination:/");
}
InfoRecord.recycle(record);
}
}
// 回调接口
interface ProcessCallback {
void processDisappeared(ViewHolder viewHolder, @NonNull ItemHolderInfo preInfo,
@Nullable ItemHolderInfo postInfo);
void processAppeared(ViewHolder viewHolder, @Nullable ItemHolderInfo preInfo,
ItemHolderInfo postInfo);
void processPersistent(ViewHolder viewHolder, @NonNull ItemHolderInfo preInfo,
@NonNull ItemHolderInfo postInfo);
void unused(ViewHolder holder);
}
复制代码
在ViewInfoStore#process
这个关键方法中,遍历mLayoutHolderMap
获取ViewHolder
绑定的InfoRecord
,根据不一样flag
的InfoRecord
,回调不一样的方法,进而处理不一样的动画,回调接口的实如今RecyclerView
中:
/**
* The callback to convert view info diffs into animations.
*/
private final ViewInfoStore.ProcessCallback mViewInfoProcessCallback =
new ViewInfoStore.ProcessCallback() {
@Override
public void processDisappeared(ViewHolder viewHolder, @NonNull ItemHolderInfo info,
@Nullable ItemHolderInfo postInfo) {
// 先移除缓存中的ViewHolder
mRecycler.unscrapView(viewHolder);
animateDisappearance(viewHolder, info, postInfo);
}
@Override
public void processAppeared(ViewHolder viewHolder,
ItemHolderInfo preInfo, ItemHolderInfo info) {
// 出现的动画
animateAppearance(viewHolder, preInfo, info);
}
@Override
public void processPersistent(ViewHolder viewHolder,
@NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo) {
viewHolder.setIsRecyclable(false);
if (mDataSetHasChangedAfterLayout) {
if (mItemAnimator.animateChange(viewHolder, viewHolder, preInfo,
postInfo)) {
postAnimationRunner();
}
} else if (mItemAnimator.animatePersistence(viewHolder, preInfo, postInfo)) {
postAnimationRunner();
}
}
@Override
public void unused(ViewHolder viewHolder) {
mLayout.removeAndRecycleView(viewHolder.itemView, mRecycler);
}
};
复制代码
若是你注意到删除方法,你的疑问就更大了,删除的子视图都没了,还执行毛线删除动画?那我就得告诉你了:虽然当前RecyclerView
没有须要删除的子视图,可是当前的ViewInfoStore
有ViewHolder
啊,因此在执行删除动画前会将ViewHolder
中的子视图从新添加到RecyclerView
里面,这里看一下上面的processDisappeared
方法调用的RecyclerView#animateDisappearance
方法,来看看是否是这样的:
void animateDisappearance(@NonNull ViewHolder holder,
@NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo) {
// 将子视图从新加入到界面
addAnimatingView(holder);
holder.setIsRecyclable(false);
// mItemAnimator执行删除动画
if (mItemAnimator.animateDisappearance(holder, preLayoutInfo, postLayoutInfo)) {
postAnimationRunner();
}
}
/**
* 将删除的子视图从新添加进界面
*/
private void addAnimatingView(ViewHolder viewHolder) {
final View view = viewHolder.itemView;
final Boolean alreadyParented = view.getParent() == this;
mRecycler.unscrapView(getChildViewHolder(view));
if (viewHolder.isTmpDetached()) {
// 从新attach回界面
mChildHelper.attachViewToParent(view, -1, view.getLayoutParams(), true);
} else if (!alreadyParented) {
// 添加视图
mChildHelper.addView(view, true);
} else {
mChildHelper.hide(view);
}
}
复制代码
一图了解当前界面ViewHolder的状态和须要执行的动画:
mItemAnimator
,其余动画也是如此。
ItemAnimator
是一个抽象类,因此一些方法须要具体的类实现,在没有指定具体的ItemAnimator
状况下,系统使用了默认的DefaultItemAnimator
。一图简单了解DefaultItemAnimator
机制:
DefaultItemAnimator
,你还能够自定义一个
ItemAnimator
,主要实现增、删、更新和移动等一些方法,本文就再也不深刻了,感兴趣的同窗能够自行研究。
在DefaultItemAnimator
的删除动画中,会对被删除的子视图执行透明度1-0的动画,动画结束后,会删除子视图和回收ViewHolder
,位移动画没有放在透明度动画结束后调用,而是使用时间为透明度动画执行时间的延迟,因此看上去就像子视图被删除后下面的子视图才开始网上位移的。
动画执行完毕之后,图片就变成了:
以上就是RecyclerView
删除部分的Adapter
和ItemAnimator
的调用原理,其余方法同窗们能够自行分析~
若是你想继续了解RecyclcerView
:
第一篇:《抽丝剥茧RecyclerView - 化整为零》
第二篇:《抽丝剥茧RecyclerView - LayoutManager》
特别分享篇: