列表页加载完毕,一个列表页A含有 10 个 item, 轻轻向上滑动,RecyclerView 做了哪些操作?
今天结合源码分析一下这个场景
文章分析基于RecyclerView API 25
仅分析上面这一个场景
答案:
4 个,额外执行 4 次createViewHolder()方法
为什么?
总结如下:
这个场景只涉及mCachedView和recycledViewPool这两个集合
当我们在屏幕上滑动的时候,移除的 ViewHolder 会首先放入mCachedViews,mCachedViews不满,ViewHolder 不会放入RecycledViewPool中
预加载在onTouchEvent()方法中
mGapWorker.postFromTraversal() 开始预加载流程
public class RecyclerView extends ViewGroup implements ScrollingView, NestedScrollingChild2 {
@Override
public boolean onTouchEvent(MotionEvent e) {
switch (action) {
//...
case MotionEvent.ACTION_MOVE: {
//...
if (mScrollState == SCROLL_STATE_DRAGGING) {
//...
if (mGapWorker != null && (dx != 0 || dy != 0)) {
//当你的指尖稍微一滑动 dy!=0 执行这个方法了
mGapWorker.postFromTraversal(this, dx, dy);
}
}
}
break;
}
}
}
final class GapWorker implements Runnable {
@Override
public void run() {
try {
//...
//执行预加载
prefetch(nextFrameNs);
} finally {
TraceCompat.endSection();
}
}
//
void prefetch(long deadlineNs) {
buildTaskList();
//刷新任务
flushTasksWithDeadline(deadlineNs);
}
//
private RecyclerView.ViewHolder prefetchPositionWithDeadline(RecyclerView view,
int position, long deadlineNs) {
//...
RecyclerView.Recycler recycler = view.mRecycler;
RecyclerView.ViewHolder holder;
try {
view.onEnterLayoutOrScroll();
//获取 holder
//关键代码
holder = recycler.tryGetViewHolderForPositionByDeadline(
position, false, deadlineNs);
if (holder != null) {
if (holder.isBound() && !holder.isInvalid()) {
//放入mCachedViews
recycler.recycleView(holder.itemView);
} else {
//放入recycledViewPool
recycler.addViewHolderToRecycledViewPool(holder, false);
}
}
//...
} finally {
view.onExitLayoutOrScroll(false);
}
return holder;
}
}
GapWorker 是一个 runnable,在Run() 方法被调用时,经过层层调用,最终来到prefetchPositionWithDeadline()方法
public class RecyclerView extends ViewGroup implements ScrollingView, NestedScrollingChild2 {
@Nullable
ViewHolder tryGetViewHolderForPositionByDeadline(int position,
boolean dryRun, long deadlineNs) {
//...
ViewHolder holder = null;
// 1) Find by position from scrap/hidden list/cache
if (holder == null) {
// 从mCachedView获取viewHolder
holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun);
//...
}
//...
if (holder == null) {
//...
if (holder == null) {
//...
// 回调Adapter.createViewHolder()方法
holder = mAdapter.createViewHolder(RecyclerView.this, type);
}
}
//...
return holder;
}
}
因此 一上来prefetch 首先会 执行
createViewHolder
1.执行createViewHolder逻辑创建一个ViewHolder,顺手放入mCachedViews
2.此时ViewHolder在集合mCachedViews中,还没在屏幕显示
3.屏幕接着滑动,当layoutChunk()方法调用的时候, 直接从mCachedViews中获取,填充 item. 屏幕显示
4.再次执行步骤 1即预加载流程,如此往复,直至recycledViewPool中含有 ViewHolder 就不在创建ViewHolder 了. 当然一共循环 4 次.
这样的好处是在layoutChunk()填充 item 的时候,不用立即创建,而是用创建好的,滑动更流畅,更丝滑.
其他知识点
一次,因为onItemRangeChanged()方法的返回值决定是否执行刷新,为 true 的条件就是list 的 size==1,
因此不管在一次点击事件中notifyItemChanged()几次,只有一次刷新.
class AdapterHelper implements OpReorderer.Callback {
//......
/**
* @return True if updates should be processed.
*/
boolean onItemRangeChanged(int positionStart, int itemCount, Object payload) {
if (itemCount < 1) {
return false;
}
mPendingUpdates.add(obtainUpdateOp(UpdateOp.UPDATE, positionStart, itemCount, payload));
mExistingUpdateTypes |= UpdateOp.UPDATE;
// 这里只有 size=1 的时候 return,也就是说,若是开发者adapter.notifyItemChanged()方法
// 同时调用 2 次以上,只有一次生效.即只会刷新一次
return mPendingUpdates.size() == 1;
}
//......
}
更多内容 欢迎关注公众号
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有