
RecyclerView 的局部刷新通过 notifyItemChanged()、notifyItemInserted() 等系列方法实现,核心原理是只更新变化的元素,复用未变化的元素,从而避免全量刷新带来的性能开销。
一、局部刷新 vs 全局刷新
| 刷新方式 | 方法 | 行为 | 性能 |
|---|---|---|---|
| 全局刷新 | notifyDataSetChanged() |
重新绑定所有 item | 差(可能闪烁) |
| 局部刷新 | notifyItemChanged(position) |
只更新指定位置 | 好(无闪烁) |
// 全局刷新 - 不推荐
adapter.notifyDataSetChanged();
// 局部刷新 - 推荐
adapter.notifyItemChanged(5); // 刷新第5个item
adapter.notifyItemInserted(3); // 在第3个位置插入新item
adapter.notifyItemRemoved(10); // 删除第10个item
adapter.notifyItemMoved(2, 8); // 移动item
adapter.notifyItemRangeChanged(0, 5); // 刷新0-5共6个item
二、源码层面分析
1. notifyItemChanged 调用链
// RecyclerView.Adapter
public final void notifyItemChanged(int position) {
// 1. 通知观察者(RecyclerViewDataObserver)
mObservable.notifyItemRangeChanged(position, 1);
}
// RecyclerViewDataObserver
public void onItemRangeChanged(int positionStart, int itemCount) {
// 2. 标记需要更新
RecyclerView.this.viewRangeUpdate(positionStart, itemCount);
}
// RecyclerView
void viewRangeUpdate(int positionStart, int itemCount) {
// 3. 计算受影响的 ViewHolder
for (int i = 0; i < mChildHelper.getChildCount(); i++) {
ViewHolder holder = getChildViewHolderInt(child);
if (holder.getLayoutPosition() >= positionStart
&& holder.getLayoutPosition() < positionStart + itemCount) {
// 4. 标记这些 ViewHolder 需要更新
holder.addFlags(ViewHolder.FLAG_UPDATE);
}
}
// 5. 请求重新布局
requestLayout();
}
2. 布局阶段 - 局部更新的体现
// RecyclerView 布局时
void onLayoutChildren(RecyclerView.Recycler recycler, State state) {
// ... 布局逻辑
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
ViewHolder holder = getChildViewHolderInt(child);
if (holder.isUpdated()) { // 被标记为需要更新
// 重新绑定数据(调用 onBindViewHolder)
adapter.bindViewHolder(holder, holder.getLayoutPosition());
}
// 未标记的 ViewHolder 直接复用,不调用 onBindViewHolder
}
}
三、局部刷新的关键类
1. ViewHolder 中的标志位
public static abstract class ViewHolder {
static final int FLAG_UPDATE = 1 << 0; // 需要更新
static final int FLAG_INVALID = 1 << 1; // 已失效
static final int FLAG_REMOVED = 1 << 2; // 已删除
static final int FLAG_IGNORE = 1 << 3; // 需要忽略
// ...
int mFlags;
boolean isUpdated() {
return (mFlags & FLAG_UPDATE) != 0;
}
}
2. Adapter 的观察者模式
// AdapterDataObservable - 观察者模式
public class AdapterDataObservable extends Observable<AdapterDataObserver> {
public void notifyItemRangeChanged(int positionStart, int itemCount) {
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onItemRangeChanged(positionStart, itemCount);
}
}
}
四、局部刷新的执行流程

五、不同刷新方法的区别
| 方法 | 标志位 | 复用行为 | 动画 |
|---|---|---|---|
notifyItemChanged(pos) |
FLAG_UPDATE |
重新绑定数据 | 有变化动画 |
notifyItemInserted(pos) |
新增 ViewHolder | 创建/复用新 VH | 有插入动画 |
notifyItemRemoved(pos) |
FLAG_REMOVED |
删除 ViewHolder | 有删除动画 |
notifyItemMoved(from, to) |
移动位置 | 重新布局 | 有移动动画 |
notifyDataSetChanged() |
FLAG_INVALID |
全部重新创建 | 无动画 |
其实RecyclerView 局部刷新通过给 ViewHolder 设置 FLAG_UPDATE 标志位,在下一帧布局时只对被标记的 ViewHolder 重新调用 onBindViewHolder,未标记的直接复用,从而实现增量更新。