RecyclerView介绍
RecyclerView 是作为 ListView 和 GridView 的加强版出现的,目的是在有限的屏幕之上展示大量的内容,因此 RecyclerView 的复用机制的实现是它的一个核心部分,复用机制可以显著提高性能,改善应用响应能力并降低功耗。
注意: RecyclerView 除了是类的名称,也是库的名称。RecyclerView 始终表示 RecyclerView 库中的类。
专业术语
- Adapter:RecyclerView.Adapter负责提供表示数据集中项目的视图的子类。
- Position:数据项在Adapter 中的位置。
- Index:在调用中使用的附加子视图的索引 ViewGroup.getChildAt(int)。与位置相对。
- Binding:准备子视图以显示与适配器内某个位置对应的数据的过程。
- Recycle(view):Recycler 负责管理报废或分离的项目视图以供重用。
- Scrap(view):在布局过程中进入临时分离状态的子视图。废弃视图可以被重用,而不会与父 RecyclerView 完全分离,如果不需要重新绑定则不修改,或者如果视图被认为是脏的,则由适配器修改。
- Dirty(view):必须由适配器重新绑定才能显示的子视图。
常用方法
- setLayoutManager:必选项,设置 RecyclerView 的布局管理器,决定RecyclerView 的显示风格。常用的有线性布局管理器(LinearLayoutManager)、网格布局管理器(GridLayoutManager)、瀑布流布局管理器(StaggeredGridLayoutManager)。
- setAdapter:必选项,设置 RecyclerView 的数据适配器。当数据发生改变时,以通知者的身份,通知 RecyclerView 数据改变进行列表刷新操作。
- addItemDecoration:非必选项,设置 RecyclerView中 Item 的装饰器,经常用来设置 Item 的分割线。
- setItemAnimator:非必选项,设置 RecyclerView中 Item 的动画。
定义Adapter
你需要替换三个关键方法:
- onCreateViewHolder():每当 RecyclerView 需要创建新的 ViewHolder 时,它都会调用此方法。此方法会创建并初始化 ViewHolder 及其关联的 View,但不会填充视图的内容,因为 ViewHolder 此时尚未绑定到具体数据。
- onBindViewHolder():RecyclerView 调用此方法将 ViewHolder 与数据相关联。此方法会提取适当的数据,并使用该数据填充 ViewHolder 的布局。例如,如果 RecyclerView 显示的是一个名称列表,该方法可能会在列表中查找适当的名称,并填充 ViewHolder 的 TextView widget。
- getItemCount():RecyclerView 调用此方法来获取数据集的大小。例如,在通讯簿应用中,这可能是地址总数。RecyclerView 使用此方法来确定什么时候没有更多的列表项可以显示。
实例
效果图
代码实现
RecyclerViewActivity
public class RecyclerViewActivity extends ActivityBase{
ActivityRecyclerviewBinding recyclerviewBinding;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
recyclerviewBinding = ActivityRecyclerviewBinding.inflate(getLayoutInflater());
setContentView(recyclerviewBinding.getRoot());
LinearLayoutManager llManager = new LinearLayoutManager(this);
//默认垂直展示,这里设置为水平显示
llManager.setOrientation(LinearLayoutManager.HORIZONTAL);
//设置 RecyclerView 的布局管理器
recyclerviewBinding.rvUserinfo.setLayoutManager(llManager);
List<UserInfo> userList = new ArrayList<>();
userList.add(new UserInfo("苏妲己", R.mipmap.scc_1));
...
userList.add(new UserInfo("王昭君", R.mipmap.scc_7));
BaseRecyclerAdapter<UserInfo> adapter = new BaseRecyclerAdapter<UserInfo>(this,userList) {
@Override
public int getItemLayoutId(int viewType) {
return R.layout.item_rv_userinfo;
}
@Override
public void convert(RecyclerViewHolder holder, int position, UserInfo bean) {
holder.setText(R.id.tv_name,bean.getName());
holder.setText(R.id.tv_content,bean.getContent());
holder.setImageResource(R.id.iv_head,bean.getHead());
}
};
//设置 RecyclerView 的数据适配器。
recyclerviewBinding.rvUserinfo.setAdapter(adapter);
//addItemDecoration
recyclerviewBinding.rvUserinfo.addItemDecoration(new RvDividerItemDecoration(this,10));
}
}
复制代码
activity_recyclerview.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_userinfo"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
复制代码
item_rv_userinfo.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="match_parent">
<ImageView
android:id="@+id/iv_head"
.../>
<TextView
android:id="@+id/tv_name"
.../>
<TextView
android:id="@+id/tv_content"
.../>
</RelativeLayout>
复制代码
BaseRecyclerAdapter
public abstract class BaseRecyclerAdapter<T> extends RecyclerView.Adapter<RecyclerViewHolder> {
protected final List<T> mData;
protected final Context mContext;
protected LayoutInflater mInflater;
private OnItemClickListener mClickListener;
private OnItemLongClickListener mLongClickListener;
public BaseRecyclerAdapter(Context ctx, List<T> list) {
mData = (list != null) ? list : new ArrayList<T>();
mContext = ctx;
mInflater = LayoutInflater.from(ctx);
}
@Override
public RecyclerViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
////创建一个新视图,该视图定义item的UI
final RecyclerViewHolder holder = new RecyclerViewHolder(mContext,
mInflater.inflate(getItemLayoutId(viewType), parent, false));
if (mClickListener != null) {
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mClickListener.onItemClick(holder.itemView, holder.getLayoutPosition());
}
});
}
if (mLongClickListener != null) {
holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
mLongClickListener.onItemLongClick(holder.itemView, holder.getLayoutPosition());
return true;
}
});
}
return holder;
}
@Override
public void onBindViewHolder(RecyclerViewHolder holder, int position) {
convert(holder, position, mData.get(position));
}
@Override
public int getItemCount() {
return mData.size();
}
public void add(int pos, T item) {
mData.add(pos, item);
notifyItemInserted(pos);
}
public void delete(int pos) {
mData.remove(pos);
notifyItemRemoved(pos);
}
public void setOnItemClickListener(OnItemClickListener listener) {
mClickListener = listener;
}
public void setOnItemLongClickListener(OnItemLongClickListener listener) {
mLongClickListener = listener;
}
abstract public int getItemLayoutId(int viewType);
abstract public void convert(RecyclerViewHolder holder, int position, T bean);
public interface OnItemClickListener {
public void onItemClick(View itemView, int pos);
}
public interface OnItemLongClickListener {
public void onItemLongClick(View itemView, int pos);
}
}
复制代码
RecyclerViewHolder
public class RecyclerViewHolder extends RecyclerView.ViewHolder {
private SparseArray<View> mViews;//集合类,layout里包含的View,以view的id作为key,value是view对象
private Context mContext;//上下文对象
public RecyclerViewHolder(Context ctx, View itemView) {
super(itemView);
mContext = ctx;
mViews = new SparseArray<View>();
}
private <T extends View> T findViewById(int viewId) {
View view = mViews.get(viewId);
if (view == null) {
view = itemView.findViewById(viewId);
mViews.put(viewId, view);
}
return (T) view;
}
public View getView(int viewId) {
return findViewById(viewId);
}
public TextView getTextView(int viewId) {
return (TextView) getView(viewId);
}
public Button getButton(int viewId) {
return (Button) getView(viewId);
}
public ImageView getImageView(int viewId) {
return (ImageView) getView(viewId);
}
public ImageButton getImageButton(int viewId) {
return (ImageButton) getView(viewId);
}
public EditText getEditText(int viewId) {
return (EditText) getView(viewId);
}
public RecyclerViewHolder setText(int viewId, String value) {
TextView view = findViewById(viewId);
view.setText(MStringUtils.isNullOrEmpty(value) ? "" : value);
return this;
}
public RecyclerViewHolder setVisible(int viewId, int visible) {
View view = findViewById(viewId);
view.setVisibility(visible);
return this;
}
public RecyclerViewHolder setBackground(int viewId, int resId) {
View view = findViewById(viewId);
view.setBackgroundResource(resId);
return this;
}
public RecyclerViewHolder setImageResource(int viewId, int resId) {
ImageView view = findViewById(viewId);
view.setImageResource(resId);
return this;
}
public RecyclerViewHolder setClickListener(int viewId, View.OnClickListener listener) {
View view = findViewById(viewId);
view.setOnClickListener(listener);
return this;
}
}
复制代码
XRecyclerView
一个 RecyclerView 实现了 pullrefresh 、loadingmore 和 header featrues。你可以像标准的 RecyclerView 一样使用它。 你不需要实现一个特殊的适配器。
上面了解到了RecyclerView的简单使用,下面咱来看看其源码。比较现在要求较高不能光会用还得知道其原理。
RecyclerView源码一万多行,想全部读懂学会挺麻烦的,感兴趣的可以自己去瞅瞅,这篇文章重点来看下 RecyclerView是如何一步步将每一个 ItemView 显示到屏幕上,然后再分析在显示和滑动过程中,是如何通过缓存复用来提升整体性能的。
RecyclerView本质上也是一个自定义控件,因此我们可以沿着分析其 onMeasure -> onLayout -> onDraw 这 3 个方法的路线来深入研究。
绘制流程分析
onMeasure
@Override
protected void onMeasure(int widthSpec, int heightSpec) {
...
if (mLayout.isAutoMeasureEnabled()) {
final int widthMode = MeasureSpec.getMode(widthSpec);
final int heightMode = MeasureSpec.getMode(heightSpec);
//mLayout(传入的 LayoutManager)的 onMeasure 方法测量RecyclerView的宽高。
mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
//RecyclerView 的宽高被设置为 match_parent 或者具体值,则返回 true
final boolean measureSpecModeIsExactly =
widthMode == MeasureSpec.EXACTLY && heightMode == MeasureSpec.EXACTLY;
if (measureSpecModeIsExactly || mAdapter == null) {
return;
}
if (mState.mLayoutStep == State.STEP_START) {
//动画相关
dispatchLayoutStep1();
}
mLayout.setMeasureSpecs(widthSpec, heightSpec);
mState.mIsMeasuring = true;
//RecyclerView宽高设置为wrap_content
//测量 RecyclerView 的子 View 的大小,最终确定 RecyclerView 的实际宽高。
dispatchLayoutStep2();
...
}
}
复制代码
onLayout
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
TraceCompat.beginSection(TRACE_ON_LAYOUT_TAG);
dispatchLayout();
TraceCompat.endSection();
mFirstLayoutComplete = true;
}
复制代码
就调用了 dispatchLayout() 方法,咱们朝里面瞅
dispatchLayout
void dispatchLayout() {
...
mState.mIsMeasuring = false;
if (mState.mLayoutStep == State.STEP_START) {
dispatchLayoutStep1();
mLayout.setExactMeasureSpecsFrom(this);
//测量子 View
dispatchLayoutStep2();
} else if (mAdapterHelper.hasUpdates() || mLayout.getWidth() != getWidth()
|| mLayout.getHeight() != getHeight()) {
mLayout.setExactMeasureSpecsFrom(this);
//测量子 View
dispatchLayoutStep2();
} else {
mLayout.setExactMeasureSpecsFrom(this);
}
//触发动画效果
dispatchLayoutStep3();
}
复制代码
如果在 onMeasure 阶段没有执行 dispatchLayoutStep2() 方法去测量子 View,则会在 onLayout 阶段重新执行。
dispatchLayoutStep2
//在此步骤中,我们对最终状态的视图进行实际布局。
//如有必要,可多次运行此步骤(例如,measure)。
private void dispatchLayoutStep2() {
...
// Step 2: Run layout
mState.mInPreLayout = false;
mLayout.onLayoutChildren(mRecycler, mState);
...
}
public void onLayoutChildren(Recycler recycler, State state) {
Log.e(TAG, "You must override onLayoutChildren(Recycler recycler, State state) ");
}
复制代码
核心逻辑是调用了 mLayout 的 onLayoutChildren 方法。这个方法在 RecyclerView.LayoutManager 中的一个空方法,主要作用是测量 RecyclerView 内的子 View 大小,并确定它们所在的位置。LinearLayoutManager、GridLayoutManager,以及 StaggeredLayoutManager 都分别复写了这个方法,并实现了不同方式的布局。
LinearLayoutManager.onLayoutChildren
@Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
...
//调用 fill 方法,完成子 View 的测量布局工作;
fill(recycler, mLayoutState, state, false);
...
}
//重点
int fill(RecyclerView.Recycler recycler, LayoutState layoutState,
RecyclerView.State state, boolean stopOnFocusable) {
...
while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)) {
layoutChunkResult.resetInternal();
if (RecyclerView.VERBOSE_TRACING) {
TraceCompat.beginSection("LLM LayoutChunk");
}
//子 View 测量布局的真正实现,每次执行完之后需要重新计算 remainingSpace。
layoutChunk(recycler, state, layoutState, layoutChunkResult);
...
if (!layoutChunkResult.mIgnoreConsumed || layoutState.mScrapList != null
|| !state.isPreLayout()) {
layoutState.mAvailable -= layoutChunkResult.mConsumed;
// 我们保留一个单独的剩余空间,因为Mavaailable对于回收很重要
//每次循环之后,都将remainingSpace减去已消费的size
remainingSpace -= layoutChunkResult.mConsumed;
}
...
return start - layoutState.mAvailable;
}
复制代码
- 在 onLayoutChildren 中调用 fill 方法,完成子 View 的测量布局工作;
- 在 fill 方法中通过 while 循环判断是否还有剩余足够空间来绘制一个完整的子 View;
- layoutChunk 方法中是子 View 测量布局的真正实现,每次执行完之后需要重新计算 remainingSpace。
layoutChunk
layoutChunk 是一个非常核心的方法,这个方法执行一次就填充一个 ItemView 到 RecyclerView,部分代码如下:
void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state,
LayoutState layoutState, LayoutChunkResult result) {
//从缓存(Recycler)中取出子 ItemView。
View view = layoutState.next(recycler);
...
RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) view.getLayoutParams();
//然后调用 addView 或者 addDisappearingView 将子 ItemView 添加到 RecyclerView 中。
if (layoutState.mScrapList == null) {
if (mShouldReverseLayout == (layoutState.mLayoutDirection
== LayoutState.LAYOUT_START)) {
addView(view);
} else {
addView(view, 0);
}
} else {
if (mShouldReverseLayout == (layoutState.mLayoutDirection
== LayoutState.LAYOUT_START)) {
addDisappearingView(view);
} else {
addDisappearingView(view, 0);
}
}
//测量被添加的 RecyclerView 中的子 ItemView 的宽高。
measureChildWithMargins(view, 0, 0);
...
//根据所设置的 Decoration、Margins 等所有选项确定子 ItemView 的显示位置。
layoutDecoratedWithMargins(view, left, top, right, bottom);
...
}
复制代码
onDraw
测量和布局都完成之后,就剩下最后的绘制操作了。
@Override
public void onDraw(Canvas c) {
super.onDraw(c);
final int count = mItemDecorations.size();
for (int i = 0; i < count; i++) {
mItemDecorations.get(i).onDraw(c, this, mState);
}
}
复制代码
如果有添加 ItemDecoration,则循环调用所有的 Decoration 的 onDraw 方法,将其显示。至于所有的子 ItemView 则是通过 Android 渲染机制递归的调用子 ItemView 的 draw 方法显示到屏幕上。
绘制流程小结
RecyclerView 会将测量 onMeasure 和布局 onLayout 的工作委托给 LayoutManager 来执行,不同的 LayoutManager 会有不同风格的布局显示,这是一种策略模式。如下图:

缓存复用原理 Recycler
缓存复用是 RecyclerView 中另一个非常重要的机制,这套机制主要实现了 ViewHolder 的缓存以及复用。
核心代码是在 Recycler 中完成的,它是 RecyclerView 中的一个内部类,主要用来缓存屏幕内 ViewHolder 以及部分屏幕外 ViewHolder,部分代码如下:
public final class Recycler {
//未与RecyclerView分离的ViewHolder列表
final ArrayList<ViewHolder> mAttachedScrap = new ArrayList<>();
//与RecyclerView分离的ViewHolder列表
ArrayList<ViewHolder> mChangedScrap = null;
//ViewHolder缓存列表
final ArrayList<ViewHolder> mCachedViews = new ArrayList<ViewHolder>();
//ViewHolder缓存池
RecycledViewPool mRecyclerPool;
//开发者可以控制的ViewHolder缓存的帮助类
private ViewCacheExtension mViewCacheExtension;
//默认情况下缓存个数是 2
static final int DEFAULT_CACHE_SIZE = 2;
}
复制代码
Recycler 的缓存机制就是通过上图中的这些数据容器来实现的,实际上 Recycler 的缓存也是分级处理的,根据访问优先级从上到下可以分为 4 级,如下:
- 第一级缓存 mAttachedScrap&mChangedScrap
- 第二级缓存 mCachedViews
- 第三级缓存 ViewCacheExtension
- 第四级缓存 RecycledViewPool
各级缓存功能
RecyclerView 之所以要将缓存分成这么多块,是为了在功能上进行一些区分,并分别对应不同的使用场景。
第一级缓存 mAttachedScrap&mChangedScrap
是两个名为 Scrap 的 ArrayList,这两者主要用来缓存屏幕内的 ViewHolder。为什么屏幕内的 ViewHolder 需要缓存呢?
做过 App 开发的应该都熟悉通过下拉刷新列表中的内容,当刷新被触发时,只需要在原有的 ViewHolder 基础上进行重新绑定新的数据 data 即可,而这些旧的 ViewHolder 就是被保存在 mAttachedScrap 和 mChangedScrap 中。实际上当我们调用 RV 的 notifyXXX 方法时,就会向这两个列表进行填充,将旧 ViewHolder 缓存起来。
第二级缓存 mCachedViews
它用来缓存移除屏幕之外的 ViewHolder,默认情况下缓存个数是 2,不过可以通过 setViewCacheSize 方法来改变缓存的容量大小。如果 mCachedViews 的容量已满,则会根据 FIFO(先进先出) 的规则将旧 ViewHolder 抛弃,然后添加新的 ViewHolder。
通常情况下刚被移出屏幕的 ViewHolder 有可能接下来马上就会使用到,所以 RecyclerView 不会立即将其设置为无效 ViewHolder,而是会将它们保存到 cache 中,但又不能将所有移除屏幕的 ViewHolder 都视为有效 ViewHolder,所以它的默认容量只有 2 个。
第三级缓存 ViewCacheExtension
这是 RecyclerView 预留给开发人员的一个抽象类,在这个类中只有一个抽象方法。
public abstract static class ViewCacheExtension {
@Nullable
public abstract View getViewForPositionAndType(@NonNull Recycler recycler, int position,
int type);
}
复制代码
开发人员可以通过继承 ViewCacheExtension,并复写抽象方法 getViewForPositionAndType 来实现自己的缓存机制。一般情况下我们不会自己实现也不建议自己去添加缓存逻辑,因为这个类的使用门槛较高(牛掰的人请忽略)。
第四级缓存 RecycledViewPool
RecycledViewPool 同样是用来缓存屏幕外的 ViewHolder,当 mCachedViews 中的个数已满(默认为 2),则从 mCachedViews 中淘汰出来的 ViewHolder 会先缓存到 RecycledViewPool 中。ViewHolder 在被缓存到 RecycledViewPool 时,会将内部的数据清理,因此从 RecycledViewPool 中取出来的 ViewHolder 需要重新调用 onBindViewHolder 绑定数据。这就同最早的 ListView 中的使用 ViewHolder 复用 convertView 的道理是一致的,因此 RV 也算是将 ListView 的优点完美的继承过来。
RecycledViewPool 还有一个重要功能,官方对其有如下解释:
RecycledViewPool 允许您在多个 RecyclerView 之间共享视图。
如果要跨 RecyclerViews 回收视图,请创建 RecycledViewPool 的实例并使用RecyclerView.setRecycledViewPool(RecycledViewPool).
如果您不提供,RecyclerView 会自动为自己创建一个池。
可以看出,多个 RecycledView 之间可以共享一个 RecycledViewPool,这对于多 tab 界面的优化效果会很显著。需要注意的是,RecycledViewPool 是根据 type 来获取 ViewHolder,每个 type 默认最大缓存 5 个。因此多个 RecyclerView 共享 RecycledViewPool 时,必须确保共享的 RecyclerView 使用的 Adapter 是同一个,或 view type 是不会冲突的。
RecyclerView 是如何从缓存中获取 ViewHolder 的
在上面介绍 onLayout 阶段时,有介绍在 layoutChunk 方法中通过调用 layoutState.next 方法拿到某个子 ItemView,然后添加到 RecyclerView 中。
layoutState.next:
LinearLayoutManager.LayoutState.next
View next(RecyclerView.Recycler recycler) {
if (mScrapList != null) {
return nextViewFromScrapList();
}
final View view = recycler.getViewForPosition(mCurrentPosition);
mCurrentPosition += mItemDirection;
return view;
}
//继续跟getViewForPosition(mCurrentPosition);
@NonNull
public View getViewForPosition(int position) {
return getViewForPosition(position, false);
}
//继续跟getViewForPosition
View getViewForPosition(int position, boolean dryRun) {
return tryGetViewHolderForPositionByDeadline(position, dryRun, FOREVER_NS).itemView;
}
复制代码
可以看出最终调用 tryGetViewHolderForPositionByDeadline 方法来查找相应位置上的ViewHolder。
tryGetViewHolderForPositionByDeadline
在这个方法中会从上面介绍的 4 级缓存中依次查找:
@Nullable
ViewHolder tryGetViewHolderForPositionByDeadline(int position,
boolean dryRun, long deadlineNs) {
...
ViewHolder holder = null;
// 1)根据位置从scrap和mCachedViews返回位置的视图。
if (holder == null) {
//根据位置从scrap和mCachedViews返回位置的视图。
holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun);
}
if (holder == null) {
//获取这个位置的数据的类型(子View复写的方法)
final int type = mAdapter.getItemViewType(offsetPosition);
// 2)根据ID在scrap和cache 中查找
if (mAdapter.hasStableIds()) {
holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition),type, dryRun);
}
//从ViewCacheExtension获取ViewHolder
if (holder == null && mViewCacheExtension != null) {
//返回 绑定到给定位置的视图,如果没有可重用的视图,则为NULL
final View view = mViewCacheExtension.getViewForPositionAndType(this, position, type);
}
//3)从RecycledViewPool获取ViewHolder
if (holder == null) {
//4)从RecycledViewPool获取ViewHolder
holder = getRecycledViewPool().getRecycledView(type);
}
if (holder == null) {
//5)如果在各级缓存中都没有找到相应的 ViewHolder,则会使用 Adapter 中的 createViewHolder 方法创建一个新的 ViewHolder。
holder = mAdapter.createViewHolder(RecyclerView.this, type);
}
}
...
return holder;
}
复制代码
代码中1-4在各级缓存中都没有找到相应的 ViewHolder,则会使用 Adapter 中的 createViewHolder 方法创建一个新的 ViewHolder。
何时将 ViewHolder 存入缓存
接下来看下 ViewHolder 被存入各级缓存的场景。
第一次 layout
当调用 setLayoutManager 和 setAdapter 之后,RecyclerView 会经历第一次 layout 并被显示到屏幕上,此时并不会有任何 ViewHolder 的缓存,所有的 ViewHolder 都是通过 createViewHolder 创建的。
刷新列表
通过手势下拉刷新,获取到新的数据 data 之后,我们会调用 notifyXXX 方法通知 RecyclerView 数据发生改变。
RecyclerView 会先将屏幕内的所有 ViewHolder 保存在 Scrap 中。
当缓存执行完之后,后续通过 Recycler 就可以从缓存中获取相应 position 的 ViewHolder(姑且称为旧 ViewHolder)。
然后将刷新后的数据设置到这些 ViewHolder 上,再将新的 ViewHolder 绘制到 RecyclerView 。
RecyclerView 的代码极其庞大,理解 RecyclerView 的源码实现,有助于我们快速定位问题原因、拓展 RecyclerView 功能、提高分析 RecyclerView 性能问题的能力,感兴趣的可以自己去捣鼓高估。
以上就是本文的全部内容啦,希望对你有所帮助。


















![[02/27][官改] Simplicity@MIX2 ROM更新-一一网](https://www.proyy.com/wp-content/uploads/2020/02/3168457341.jpg)


![[桜井宁宁]COS和泉纱雾超可爱写真福利集-一一网](https://www.proyy.com/skycj/data/images/2020-12-13/4d3cf227a85d7e79f5d6b4efb6bde3e8.jpg)

![[桜井宁宁] 爆乳奶牛少女cos写真-一一网](https://www.proyy.com/skycj/data/images/2020-12-13/d40483e126fcf567894e89c65eaca655.jpg)