什么时候调用onCreatViewHolder、onBindViewHolder?
通过上一遍的RecyclerView的复用和回收机制文章,我们知道RecyclerView的拿取View
是通过tryGetViewHolderForPositionByDeadline
这个方法复用和创建ViewHolder
。所以Adapter的onCreatViewHolder、onBindViewHolder
的也是从此方法中执行生成ViewHolder。来看看方法
ViewHolder tryGetViewHolderForPositionByDeadline(int position,
boolean dryRun, long deadlineNs) {
//...前面四大缓存复用还是找不到holder
if (holder == null) {
// 调用Adapter.createViewHolder方法
holder = mAdapter.createViewHolder(RecyclerView.this, type);
}
// 到这里一定有了ViewHolder,根据ViewHolder的状态判断是否执行BindViewHolder绑定数据
if (mState.isPreLayout() && holder.isBound()) {
holder.mPreLayoutPosition = position;
} else if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) {
// tryBindViewHolderByDeadline 方法中执行Adapter.bindViewHolder
bound = tryBindViewHolderByDeadline(holder, offsetPosition, position, deadlineNs);
}
}
// 最后调用mAdapter.bindViewHolder绑定数据
private boolean tryBindViewHolderByDeadline(@NonNull ViewHolder holder, int offsetPosition,
int position, long deadlineNs) {
//...
mAdapter.bindViewHolder(holder, offsetPosition);
//...
return true;
}
如果在四级缓存中都没有拿到holder,则调用Adapter
的createViewHolder
去执行我们的抽象方法onCreateViewHolder
创建ViewHolder。
然后根据ViewHolder
的Flags
的状态(是否需要更新、是否已失效)决定是否调用tryBindViewHolderByDeadline
,tryBindViewHolderByDeadline
进而调用Adapter
的bindViewHolder
方法。继续往下看Adapter
的createViewHolder
和bindViewHolder
方法:
public abstract static class Adapter<VH extends ViewHolder> {
public final VH createViewHolder(@NonNull ViewGroup parent, int viewType) {
try {
TraceCompat.beginSection(TRACE_CREATE_VIEW_TAG);
// 调用onCreateViewHolder抽象方法
final VH holder = onCreateViewHolder(parent, viewType);
if (holder.itemView.getParent() != null) {
throw new IllegalStateException("ViewHolder views must not be attached when"
+ " created. Ensure that you are not passing 'true' to the attachToRoot
+ " parameter of LayoutInflater.inflate(..., boolean attachToRoot)");
}
// 给viewholder赋值viewtype
holder.mItemViewType = viewType;
return holder;
} finally {
TraceCompat.endSection();
}
}
public final void bindViewHolder(@NonNull VH holder, int position) {
boolean rootBind = holder.mBindingAdapter == null;
// 首次创建时,设置position,itemId,Flags
if (rootBind) {
holder.mPosition = position;
if (hasStableIds()) {
holder.mItemId = getItemId(position);
}
holder.setFlags(ViewHolder.FLAG_BOUND,
ViewHolder.FLAG_BOUND | ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID
| ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN);
TraceCompat.beginSection(TRACE_BIND_VIEW_TAG);
}
holder.mBindingAdapter = this;
//执行onBindViewHolder抽象方法
onBindViewHolder(holder, position, holder.getUnmodifiedPayloads());
if (rootBind) {
// payload的清除
holder.clearPayload();
final ViewGroup.LayoutParams layoutParams = holder.itemView.getLayoutParams();
if (layoutParams instanceof RecyclerView.LayoutParams) {
((LayoutParams) layoutParams).mInsetsDirty = true;
}
TraceCompat.endSection();
}
}
public abstract VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType);
public abstract void onBindViewHolder(@NonNull VH holder, int position);
}
由Adapter
的源码可见,createViewHolder
会先调用onCreateViewHolder
创建一个ViewHolder
,然后给这个ViewHolder设置ViewType。
bindViewHolder
先判断ViewHolder
是否有绑定Adapter
,如果还没有绑定Adapter
则先给ViewHolder
设置position
、ItemId
、Flags
变量。如果已经绑定的Adapter
,则更新绑定和调用onBindViewHolder
绑定数据。
代码很简单容易看懂,所以Adapter
的onCreateViewHolder
和onBindViewHolder
是在方法tryGetViewHolderForPositionByDeadline
方法被调用的,onCreateViewHolder
的调用时机是前面的四大缓存集合都没有找到可 复用的ViewHolder,所以调用onCreateViewHolder来创建一个新的ViewHolder。onBindViewHolder
的调用时机是根据holder
的Flags
的状态来判断是否执行。
为什么notifyDataSetChanged()不会执行动画,而notifyItemChanged(1)会执行动画?
1.为什么一调用Adapter的notify就会让RecyclerView执行跟新操作?
就是使用观察者模式
通知RecyclerView
去执行更新。Adapter
做为被观察者AdapterDataObservable
,RecyclerView
作为观察者RecyclerViewDataObserver
。只要Adapter的notify系列方法被被调用,就会通知观察者做相应的操作。所以,当我们调用Adapter
的notifyDataSetChanged
和notifyItemChanged(1)
其真正的操作实现方法是在观察者类RecyclerViewDataObserver
实例的方法是如何执行,看看这个类的源码。
private class RecyclerViewDataObserver extends AdapterDataObserver {
RecyclerViewDataObserver() {
}
// notifyDataSetChanged()执行的方法
@Override
public void onChanged() {
assertNotInLayoutOrScroll(null);
// 结构改变设置为true
mState.mStructureChanged = true;
// 将所有ViewHolder的Flags设置为无效的
processDataSetCompletelyChanged(true);
// 如果准备更新的集合mPendingUpdates的大小<=0时,执行requestLayout()
if (!mAdapterHelper.hasPendingUpdates()) {
requestLayout();
}
}
// notifyItemChanged(1)执行的方法
@Override
public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
assertNotInLayoutOrScroll(null);
//增加一个更新状态的UpdateOp到准备更新集合mPendingUpdates中,如果mPendingUpdates集合只有一个,就执行triggerUpdateProcessor()方法。
if (mAdapterHelper.onItemRangeChanged(positionStart, itemCount, payload)) {
// 相当于开始绘制流程
triggerUpdateProcessor();
}
}
}
通过上面代码的注释可以知道:
- 调用
notifyDataSetChanged()
方法后,就是将所有的ViewHolder
的flags
都标记为INVALID
无效状态,这也是没有执行动画的原因。并且mPendingUpdates
集合没有内容时就会执行requestLayout
开始重绘。 - 调用
notifyItemChanged(1)
方法后,先是往mPendingUpdates
集合中加入一个跟新的UpdateOp对象,并返回一个布尔值,当mPendingUpdates
的集合只有一个时,返回true
。如果返回true
则执行triggerUpdateProcessor
方法,此方法也相当于开始绘制流程。
notifyDataSetChanged()
方法不执行动画的原因是因为给ViewHolder设置为INVALID
无效状态,那为什么设置了无效状态就不执行动画呢?这就要从绘制流程源码看起了。
到这里,接下来就是走requestLayout
的绘制流程了。根据上篇文章RecyclerView的绘制三大流程知道在绘制流程的测量和布局中,分别会调用dispatchLayoutStep1
、dispatchLayoutStep2
、dispatchLayoutStep3
三个方法,其作用分别为:
dispatchLayoutStep1:如果有动画,保存
未变化
之前的ViewHolder
,和ViewHolder的位置信息ItemHolderInfo
。
dispatchLayoutStep2:调用LayoutManger
实例进行测量和布局
dispatchLayoutStep3:如果有动画,保存现在
的ViewHolder
,和ViewHolder的位置信息ItemHolderInfo
。并执行动画。
来看看源码:
private void dispatchLayoutStep1() {
if (mState.mRunSimpleAnimations) {
// Step 0: Find out where all non-removed items are, pre-layout
int count = mChildHelper.getChildCount();
for (int i = 0; i < count; ++i) {
// 遍历未改变前的ViewHolder
final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
//notifyDataSetChanged()已经设置了无效状态,所以会被continue,无法保存旧ViewHolder的信息和位置
if (holder.shouldIgnore() || (holder.isInvalid() && !mAdapter.hasStableIds())) {
continue;
}
// 旧ViewHolder的位置信息
final ItemHolderInfo animationInfo = mItemAnimator
.recordPreLayoutInformation(mState, holder,
ItemAnimator.buildAdapterChangeFlagsForAnimations(holder),
holder.getUnmodifiedPayloads());
// 保存旧ViewHolder和位置信息
mViewInfoStore.addToPreLayout(holder, animationInfo);
if (mState.mTrackOldChangeHolders && holder.isUpdated() && !holder.isRemoved()
&& !holder.shouldIgnore() && !holder.isInvalid()) {
long key = getChangedHolderKey(holder);
// 根据key,保存旧ViewHolder
mViewInfoStore.addToOldChangeHolders(key, holder);
}
}
}
}
private void dispatchLayoutStep2() {
mAdapterHelper.consumeUpdatesInOnePass();
// Step 2: Run layout
mState.mInPreLayout = false;
// fill->循环 layoutChunk()->tryGetViewHolderForPositionByDeadline()
mLayout.onLayoutChildren(mRecycler, mState);
}
// 代码稍长,但逻辑不难
private void dispatchLayoutStep3() {
if (mState.mRunSimpleAnimations) {
// 遍历布局后的ViewHolder,
for (int i = mChildHelper.getChildCount() - 1; i >= 0; i--) {
ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
if (holder.shouldIgnore()) {
continue;
}
// 得到key
long key = getChangedHolderKey(holder);
// 现在ViewHolder的位置信息
final ItemHolderInfo animationInfo = mItemAnimator
.recordPostLayoutInformation(mState, holder);
//根据key得到旧ViewHolder
ViewHolder oldChangeViewHolder = mViewInfoStore.getFromOldChangeHolders(key);
if (oldChangeViewHolder != null && !oldChangeViewHolder.shouldIgnore()) {
// 如果有旧ViewHolder,执行下面代码
// 判断新旧ViewHolder是否消失
final boolean oldDisappearing = mViewInfoStore.isDisappearing(
oldChangeViewHolder);
final boolean newDisappearing = mViewInfoStore.isDisappearing(holder);
if (oldDisappearing && oldChangeViewHolder == holder) {
// 旧ViewHolder已经消失,并且新的holder跟旧的一样
// run disappear animation instead of change
// 保存新viewHolder和位置信息
mViewInfoStore.addToPostLayout(holder, animationInfo);
} else {
// 得到旧ViewHolder的位置信息
final ItemHolderInfo preInfo = mViewInfoStore.popFromPreLayout(
oldChangeViewHolder);
// we add and remove so that any post info is merged.
// 保存新viewHolder和位置信息
mViewInfoStore.addToPostLayout(holder, animationInfo);
// 新ViewHolder的位置信息
ItemHolderInfo postInfo = mViewInfoStore.popFromPostLayout(holder);
if (preInfo == null) {
handleMissingPreInfoForChangeError(key, holder, oldChangeViewHolder);
} else {
animateChange(oldChangeViewHolder, holder, preInfo, postInfo,
oldDisappearing, newDisappearing);
}
}
} else {
// 没有旧ViewHolder,就保存现在ViewHolder和其位置信息
mViewInfoStore.addToPostLayout(holder, animationInfo);
}
}
// 执行动画
// Step 4: Process view info lists and trigger animations
mViewInfoStore.process(mViewInfoProcessCallback);
}
}
notifyDataSetChanged()
的ViewHolder被设置了INVALID
无效,所以在dispatchLayoutStep1
方法中将不会保存旧ViewHolder和位置信息
,而notifyDataSetChanged(1)
则可以保存旧ViewHolder和位置信息
。
所以在dispatchLayoutStep3
中notifyDataSetChanged()
将无法得到旧ViewHolder的位置信息,所以就执行保存了改变后ViewHolder和其位置信息
。而动画是根据新旧相对位置的来执行的,所以notifyDataSetChanged()
就不可以执行动画。我们继续追查mViewInfoStore.process
执行动画的源码。
//一个为ViewHolder为key,位置信息为value的HashMap
final SimpleArrayMap<RecyclerView.ViewHolder, InfoRecord> mLayoutHolderMap = new SimpleArrayMap<>();
void process(ProcessCallback callback) {
for (int index = mLayoutHolderMap.size() - 1; index >= 0; index--) {
final RecyclerView.ViewHolder viewHolder = mLayoutHolderMap.keyAt(index);
final InfoRecord record = mLayoutHolderMap.removeAt(index);
if ((record.flags & FLAG_APPEAR_AND_DISAPPEAR) == FLAG_APPEAR_AND_DISAPPEAR) {
// Appeared then disappeared. Not useful for animations.
callback.unused(viewHolder);
} else if ((record.flags & FLAG_DISAPPEARED) != 0) {
// Set as "disappeared" by the LayoutManager (addDisappearingView)
if (record.preInfo == null) {
// similar to appear disappear but happened between different layout passes.
// this can happen when the layout manager is using auto-measure
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);
}
}
process(ProcessCallback callback)
方法先遍历mLayoutHolderMap
,mLayoutHolderMap
是一个HashMap存储着新旧的ViewHolder和ViewHolder的位置信息
,之后一个一个的根据状态执行参数接口实例
方法。那就看看参数ProcessCallback
实例。
// 动画默认为DefaultItemAnimator
ItemAnimator mItemAnimator = new DefaultItemAnimator();
private final ViewInfoStore.ProcessCallback mViewInfoProcessCallback =
new ViewInfoStore.ProcessCallback() {
@Override
public void processDisappeared(ViewHolder viewHolder, @NonNull ItemHolderInfo info,
@Nullable ItemHolderInfo postInfo) {
mRecycler.unscrapView(viewHolder);
// 此方法后续调用 mItemAnimator.animateDisappearance
animateDisappearance(viewHolder, info, postInfo);
}
@Override
public void processAppeared(ViewHolder viewHolder,
ItemHolderInfo preInfo, ItemHolderInfo info) {
// 此方法后续调用 mItemAnimator.animateAppearance
animateAppearance(viewHolder, preInfo, info);
}
@Override
public void processPersistent(ViewHolder viewHolder,
@NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo) {
viewHolder.setIsRecyclable(false);
if (mDataSetHasChangedAfterLayout) {
// 调用mItemAnimator.animateChange
if (mItemAnimator.animateChange(viewHolder, viewHolder, preInfo,
postInfo)) {
// 调用动画执行方法
postAnimationRunner();
}
} else if (mItemAnimator.animatePersistence(viewHolder, preInfo, postInfo)) {
//调用了mItemAnimator.animatePersistence
// 调用动画执行方法
postAnimationRunner();
}
}
// 这个方法并没有调用动画,而是进行了ViewHolder回收
@Override
public void unused(ViewHolder viewHolder) {
mLayout.removeAndRecycleView(viewHolder.itemView, mRecycler);
}
};
void animateDisappearance(@NonNull ViewHolder holder,
@NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo) {
addAnimatingView(holder);
holder.setIsRecyclable(false);
if (mItemAnimator.animateDisappearance(holder, preLayoutInfo, postLayoutInfo)) {
// 调用动画执行方法
postAnimationRunner();
}
}
void animateAppearance(@NonNull ViewHolder itemHolder,
@Nullable ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo) {
itemHolder.setIsRecyclable(false);
if (mItemAnimator.animateAppearance(itemHolder, preLayoutInfo, postLayoutInfo)) {
// 调用动画执行方法
postAnimationRunner();
}
}
void postAnimationRunner() {
if (!mPostedAnimatorRunner && mIsAttached) {
// 创建一个异步任务执行动画方法
ViewCompat.postOnAnimation(this, mItemAnimatorRunner);
mPostedAnimatorRunner = true;
}
}
private Runnable mItemAnimatorRunner = new Runnable() {
@Override
public void run() {
if (mItemAnimator != null) {
// 调用动画的执行方法
mItemAnimator.runPendingAnimations();
}
mPostedAnimatorRunner = false;
}
};
上面代码逻辑也不难,就是每个方法相应的调用动画类ItemAnimator
的animateDisappearance
、animateAppearance
、animatePersistence
、animateChange
抽象方法。最后执行postAnimationRunner()
去创建一个异步任务去执行temAnimator.runPendingAnimations()
动画。至于unused
则没有调用ItemAnimator的方法,而是给了CacheView、RecylerViewPool回收ViewHolder。所以ProcessCallback
的接口实例除unused()
方法外,其他方法都是调用ItemAnimator
的方法进行动画。而动画是根据新旧ViewHolder
相关信息进行的,notifyDataSetChanged()
没有旧ViewHolder
自然没有参照物执行不了动画。
看看动画实例类DefaultItemAnimator
的最终执行change
的方法:
@Override
public boolean animateChange(RecyclerView.ViewHolder oldHolder, RecyclerView.ViewHolder newHolder,
int fromX, int fromY, int toX, int toY) {
if (oldHolder == newHolder) {
// Don't know how to run change animations when the same view holder is re-used.
// run a move animation to handle position changes.
return animateMove(oldHolder, fromX, fromY, toX, toY);
}
final float prevTranslationX = oldHolder.itemView.getTranslationX();
final float prevTranslationY = oldHolder.itemView.getTranslationY();
final float prevAlpha = oldHolder.itemView.getAlpha();
resetAnimation(oldHolder);
int deltaX = (int) (toX - fromX - prevTranslationX);
int deltaY = (int) (toY - fromY - prevTranslationY);
// recover prev translation state after ending animation
oldHolder.itemView.setTranslationX(prevTranslationX);
oldHolder.itemView.setTranslationY(prevTranslationY);
oldHolder.itemView.setAlpha(prevAlpha);
if (newHolder != null) {
// carry over translation values
resetAnimation(newHolder);
newHolder.itemView.setTranslationX(-deltaX);
newHolder.itemView.setTranslationY(-deltaY);
newHolder.itemView.setAlpha(0);
}
mPendingChanges.add(new ChangeInfo(oldHolder, newHolder, fromX, fromY, toX, toY));
return true;
}
这里的oldHolder == newHolder
时,会调用animateMove
的方法,这不就是调用notifyDataSetChanged
执行的吗,所以我们继续看animateMove
方法。
@Override
public boolean animateMove(final RecyclerView.ViewHolder holder, int fromX, int fromY,
int toX, int toY) {
final View view = holder.itemView;
fromX += (int) holder.itemView.getTranslationX();
fromY += (int) holder.itemView.getTranslationY();
resetAnimation(holder);
int deltaX = toX - fromX;
int deltaY = toY - fromY;
// 新旧viewHolder一样,所以都等于0
if (deltaX == 0 && deltaY == 0) {
dispatchMoveFinished(holder);
return false;
}
if (deltaX != 0) {
view.setTranslationX(-deltaX);
}
if (deltaY != 0) {
view.setTranslationY(-deltaY);
}
mPendingMoves.add(new MoveInfo(holder, fromX, fromY, toX, toY));
return true;
}
我们的新旧ViewHolder
都一样,所以deltaX、deltaY
都为0,由代码可以看出执行了dispatchMoveFinished
和 return false
,就是不往下执行动画。这就是notifyDataSetChanged
不执行动画原因。
下一篇来分析RecyclerView
的动画的实现类源码。
网友评论