RecyclerView 局部刷新的原理

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,未标记的直接复用,从而实现增量更新。

相关推荐
XiaoLeisj2 个月前
Android 短视频项目首页开发实战:从广场页广告轮播与网格列表,到发现页分类、播单与话题广场的数据驱动实现
android·okhttp·mvvm·recyclerview·retrofit·databinding·xbanner 轮播
低调小一3 个月前
RecyclerView 缓存与复用机制:从一次滑动讲明白(2026 版)
android·recyclerview
aidou13144 个月前
Android中RecyclerView实现多级列表
android·recyclerview·多级列表·layoutmanager
袁震7 个月前
Android-Compose 列表组件详解
android·recyclerview·compose
胖虎19 个月前
Android入门到实战(八):从发现页到详情页——跳转、传值与RecyclerView多类型布局
android·recyclerview·多类型布局
_小记杂七杂八1 年前
记录跟随recyclerview滑动的指示器
recyclerview·指示器·滑动指示器
xun-ming1 年前
逗号分隔、多级位置及局部更新的Sql实现
数据库·sql·局部更新·三级位置·逗号分隔
镰刀出海2 年前
Recyclerview缓存原理
java·开发语言·缓存·recyclerview·android面试