一、RecyclerView 是什么

RecyclerView 是 Android 官方推荐用于展示大量可滚动数据列表的控件,位于 androidx.recyclerview.widget 包中。它通过视图复用机制高效管理列表项,解决了 ListView 的性能瓶颈。


二、核心架构与组件

RecyclerView 采用组合式设计,通过多个独立组件协同工作:

┌─────────────────────────────────────┐
│           RecyclerView              │
│  ┌─────────────────────────────┐    │
│  │      LayoutManager          │    │  ← 负责布局排列(线性/网格/瀑布流)
│  │   (Linear/Grid/Staggered)   │    │
│  └─────────────────────────────┘    │
│  ┌─────────────────────────────┐    │
│  │         Adapter             │    │  ← 数据 ↔ 视图的绑定桥梁
│  │    (数据提供 + View创建)     │    │
│  └─────────────────────────────┘    │
│  ┌─────────────────────────────┐    │
│  │       ViewHolder            │    │  ← 缓存视图引用,避免 findViewById
│  │    (itemView + 子视图缓存)   │    │
│  └─────────────────────────────┘    │
│  ┌─────────────────────────────┐    │
│  │     ItemDecoration          │    │  ← 分割线、间距、高亮等装饰
│  └─────────────────────────────┘    │
│  ┌─────────────────────────────┐    │
│  │      ItemAnimator           │    │  ← 增删改动画(默认支持)
│  └─────────────────────────────┘    │
└─────────────────────────────────────┘

1. Adapter(适配器)

负责创建 ViewHolder 和绑定数据到视图。

kotlin

class MyAdapter(private val dataList: List<String>) : 
    RecyclerView.Adapter<MyAdapter.MyViewHolder>() {

    // 创建新的 ViewHolder(仅当没有可复用的时调用)
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
        val view = LayoutInflater.from(parent.context)
            .inflate(R.layout.item_layout, parent, false)
        return MyViewHolder(view)
    }

    // 将数据绑定到 ViewHolder
    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
        holder.bind(dataList[position])
    }

    override fun getItemCount(): Int = dataList.size

    // ViewHolder:缓存子视图引用
    class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        private val textView: TextView = itemView.findViewById(R.id.tv_text)
        
        fun bind(data: String) {
            textView.text = data
        }
    }
}

2. LayoutManager(布局管理器)

决定列表项的排列方式:

表格

类型类名特点
线性LinearLayoutManager垂直/水平单行排列,类似 ListView
网格GridLayoutManager多列网格,支持 spanSizeLookup 实现不规则跨度
瀑布流StaggeredGridLayoutManager交错网格,适合高度不一致的内容(如图片墙)
自定义继承 LayoutManager完全自定义布局逻辑

kotlin

// 垂直线性布局
recyclerView.layoutManager = LinearLayoutManager(context)

// 2列网格
recyclerView.layoutManager = GridLayoutManager(context, 2)

// 垂直瀑布流,2列
recyclerView.layoutManager = StaggeredGridLayoutManager(2, RecyclerView.VERTICAL)

3. ViewHolder(视图持有者)

  • 通过 itemView 持有列表项的根视图

  • 缓存子视图引用,避免每次绑定都调用昂贵的 findViewById

  • 必须使用静态内部类或独立类,防止内存泄漏

4. ItemDecoration(装饰)

用于绘制分割线、间距、悬浮头等:

kotlin

// 添加默认分割线
recyclerView.addItemDecoration(DividerItemDecoration(context, DividerItemDecoration.VERTICAL))

// 自定义间距
class SpaceDecoration(private val space: Int) : RecyclerView.ItemDecoration() {
    override fun getItemOffsets(
        outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State
    ) {
        outRect.set(space, space, space, space)
    }
}

5. ItemAnimator(动画)

默认使用 DefaultItemAnimator,支持增删改动画:

kotlin

// 自定义动画时长
(recyclerView.itemAnimator as? SimpleItemAnimator)?.apply {
    addDuration = 300
    removeDuration = 300
}

三、基本使用流程

kotlin

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val recyclerView: RecyclerView = findViewById(R.id.recyclerView)
        
        // 1. 设置 LayoutManager
        recyclerView.layoutManager = LinearLayoutManager(this)
        
        // 2. 设置 Adapter
        val adapter = MyAdapter(getData())
        recyclerView.adapter = adapter
        
        // 3. (可选)设置固定高度优化
        recyclerView.setHasFixedSize(true)
        
        // 4. (可选)添加装饰和动画
        recyclerView.addItemDecoration(SpaceDecoration(16))
    }
}

四、高级用法

1. 使用 ListAdapter + DiffUtil(推荐)

ListAdapter 内置 DiffUtil,自动计算数据差异并刷新:

kotlin

class MyListAdapter : ListAdapter<String, MyListAdapter.ViewHolder>(DiffCallback()) {

    class DiffCallback : DiffUtil.ItemCallback<String>() {
        override fun areItemsTheSame(old: String, new: String): Boolean {
            return old == new  // 唯一标识判断
        }
        override fun areContentsTheSame(old: String, new: String): Boolean {
            return old == new  // 内容是否相同
        }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        // ...
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.bind(getItem(position))
    }

    class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        fun bind(item: String) { /* ... */ }
    }
}

// 使用:提交新列表,自动计算差异并刷新
adapter.submitList(newList)

2. 多类型布局(Multiple View Types)

通过 getItemViewType() 实现聊天界面、信息流等复杂场景:

kotlin

class ChatAdapter(private val messages: List<Message>) : 
    RecyclerView.Adapter<RecyclerView.ViewHolder>() {

    companion object {
        const val TYPE_SENT = 1
        const val TYPE_RECEIVED = 2
    }

    override fun getItemViewType(position: Int): Int {
        return if (messages[position].isSent) TYPE_SENT else TYPE_RECEIVED
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        return when (viewType) {
            TYPE_SENT -> SentViewHolder(...)
            else -> ReceivedViewHolder(...)
        }
    }

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        when (holder) {
            is SentViewHolder -> holder.bind(messages[position])
            is ReceivedViewHolder -> holder.bind(messages[position])
        }
    }
}

3. 拖拽排序与滑动删除

通过 ItemTouchHelper 实现:

kotlin

val itemTouchHelper = ItemTouchHelper(object : ItemTouchHelper.SimpleCallback(
    ItemTouchHelper.UP or ItemTouchHelper.DOWN,  // 拖拽方向
    ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT // 滑动删除方向
) {
    override fun onMove(
        recyclerView: RecyclerView,
        from: RecyclerView.ViewHolder,
        to: RecyclerView.ViewHolder
    ): Boolean {
        val fromPos = from.adapterPosition
        val toPos = to.adapterPosition
        Collections.swap(dataList, fromPos, toPos)
        adapter.notifyItemMoved(fromPos, toPos)
        return true
    }

    override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
        val pos = viewHolder.adapterPosition
        dataList.removeAt(pos)
        adapter.notifyItemRemoved(pos)
    }
})
itemTouchHelper.attachToRecyclerView(recyclerView)

4. 分页加载

配合 Paging 3 库实现无限滚动:

kotlin

val pagingAdapter = MyPagingAdapter()
recyclerView.adapter = pagingAdapter

lifecycleScope.launch {
    viewModel.pagingDataFlow.collectLatest { pagingData ->
        pagingAdapter.submitData(pagingData)
    }
}

五、性能优化

表格

优化手段说明
setHasFixedSize(true)如果 Adapter 内容变化不影响 RecyclerView 大小,避免额外布局计算
ViewHolder 模式必须正确使用,缓存 findViewById 结果
避免在 onBindViewHolder 中创建对象如 OnClickListener 应在 onCreateViewHolder 中设置
使用 DiffUtil / ListAdapter局部刷新,避免 notifyDataSetChanged() 全局重绘
图片加载优化使用 Glide/Coil,配合 RecyclerView 滚动时暂停加载
嵌套 RecyclerView 复用使用 setRecycledViewPool() 共享 ViewHolder 池
异步布局计算AsyncLayoutInflater 预加载布局
预加载GapWorker 默认预加载,或通过 LayoutManager 自定义

常见反模式 ❌

kotlin

// ❌ 错误:在 onBind 中设置点击监听(每次绑定都创建新对象)
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
    holder.itemView.setOnClickListener { /* ... */ }  // 不要这样做!
}

// ✅ 正确:在 onCreateViewHolder 中设置,通过 adapterPosition 获取位置
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
    val holder = ViewHolder(...)
    holder.itemView.setOnClickListener {
        val pos = holder.adapterPosition
        if (pos != RecyclerView.NO_POSITION) {
            onItemClick(pos)
        }
    }
    return holder
}

六、RecyclerView vs ListView

表格

特性ListViewRecyclerView
ViewHolder官方推荐但非强制强制使用
布局类型仅垂直列表线性/网格/瀑布流/自定义
动画无内置内置 ItemAnimator
分割线divider 属性ItemDecoration 灵活定制
点击监听setOnItemClickListener需在 ViewHolder 中手动设置
局部刷新不支持notifyItemXXX() 系列方法
复用机制两级缓存四级缓存(更强大)

七、RecyclerView 四级缓存机制

RecyclerView 的缓存系统是其高性能的核心:

plain

┌─────────────────────────────────────────────────────────┐
│  1. mChangedScrap / mAttachedScrap                      │
│     → 布局期间临时缓存,同一帧内复用(如调用 notifyDataSetChanged)│
├─────────────────────────────────────────────────────────┤
│  2. mCachedViews (默认容量 = 2)                          │
│     → 刚滑出屏幕的 ViewHolder,按位置缓存,可直接复用           │
├─────────────────────────────────────────────────────────┤
│  3. mViewCacheExtension (可选,需自定义)                  │
│     → 开发者自定义缓存策略,如 ViewPager 内嵌 RecyclerView     │
├─────────────────────────────────────────────────────────┤
│  4. mRecycledViewPool (按 viewType 缓存,默认每种 5 个)     │
│     → 最终缓存池,ViewHolder 与位置解耦,需重新绑定数据          │
└─────────────────────────────────────────────────────────┘

八、总结

RecyclerView 是 Android 列表开发的事实标准,核心要点:

  1. 四大组件各司其职:Adapter 管数据、LayoutManager 管布局、ViewHolder 管视图缓存、ItemDecoration 管装饰

  2. 优先使用 ListAdapter + DiffUtil:自动差异计算,避免手动刷新

  3. 注意性能细节:setHasFixedSize(true)、避免 onBindViewHolder 中创建对象、正确使用点击监听

  4. 善用官方扩展库:Paging 3 分页、ConcatAdapter 多 Adapter 组合、Selection 库多选支持

更多推荐