1. RecyclerView 与 Compose 核心概念对比

在 Android 开发中,RecyclerView 和 Jetpack Compose 代表了两种截然不同的 UI 构建范式。本章从 ViewHolder vs Composable、Adapter vs LazyColumn、状态驱动差异 和 生命周期差异 四个维度进行系统性对比,为后续混用技巧奠定理论基础。

RecyclerView 与 Compose 核心概念对比 RecyclerView 体系 Adapter ViewHolder View 回收复用池 notifyItemChanged() 等 生命周期依赖 Activity/Fragment Compose 体系 LazyColumn @Composable 函数 remember / mutableStateOf 自动重组(Recomposition) 生命周期由 Composable 作用域管理 数据→UI 视图持有 vs 声明 回收复用 vs 重组 被动通知 vs 主动观察

1.1 ViewHolder vs Composable

维度RecyclerView ViewHolderCompose Composable
本质View 的持有类,通过 itemView 缓存布局引用无状态 UI 函数,每次重组时执行
复用机制视图回收池 + onBindViewHolder 重新绑定数据Composable 自身可被跳过重组,但无显式回收
绑定数据方式手动从 ViewHolder 中 findViewById 或 ViewBinding参数直接传入,通过 remember / derivedStateOf 控制
更新粒度最小到单个 View 成员变量,需主动调用 setText 等自动重组被观察状态变化的子 Composable

1.2 Adapter vs LazyColumn

Adapter 是 RecyclerView 的数据桥梁,实现了 数据模型 → ViewHolder 的映射;而 LazyColumn 是 Compose 中惰性列容器,直接接收 items 或 itemsIndexed 数据源。

关键差异: Adapter 需要手动管理 ViewHolder 创建/绑定/回收;LazyColumn 内部自动管理 @Composable 作用域 的创建与销毁。

典型代码对比

RecyclerView Adapter 片段:

class MyAdapter(private val data: List<String>) : RecyclerView.Adapter<MyAdapter.MyViewHolder>() {
    inner class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        val textView: TextView = itemView.findViewById(R.id.textView)
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
        val view = LayoutInflater.from(parent.context).inflate(R.layout.item, parent, false)
        return MyViewHolder(view)
    }

    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
        holder.textView.text = data[position]
    }

    override fun getItemCount() = data.size
}

Compose LazyColumn 片段:

@Composable
fun MyList(data: List<String>) {
    LazyColumn {
        items(data) { item ->
            Text(text = item, modifier = Modifier.padding(8.dp))
        }
    }
}
混用陷阱: 在 Compose 中直接嵌套 RecyclerView 会导致双重滚动冲突和测量异常。必须通过 AndroidView 封装,并手动处理 Modifier.fillMaxSize() 约束。

1.3 状态驱动差异

特性RecyclerView(命令式)Compose(声明式)
状态变更调用 notifyDataSetChanged() 或 notifyItemRangeChanged()状态变量(mutableStateOf)变化自动触发重组
数据流向Adapter 接收数据 → 手动刷新 → 局部更新数据作为 Composable 参数向下流动,状态向上提升
性能优化DiffUtil 异步计算差异;setHasStableIds(true)key 参数、derivedStateOf、remember 跳过非必要重组

1.4 生命周期差异

RecyclerView 的生命周期直接绑定到宿主 Activity/Fragment 的标准生命周期(onCreate → onResume → onPause → onDestroy)。Composable 则受 Composition 作用域 控制:

  • 进入 Composition:首次调用 Composable 函数。
  • 重组(Recomposition):状态变化触发的重新执行。
  • 离开 Composition:从界面树中移除(如 LazyColumn 的 item 滑出屏幕)。
最佳实践: 混用时,通过 DisposableEffect 或 AndroidViewBinding 中的 LifecycleEventObserver 桥接两个生命周期。例如在 RecyclerView 的 View 中添加 LifecycleCoroutineScope 监听。

1.5 常见错误与性能要点

  • 错误: 在 Compose 中直接修改 RecyclerView 的 Adapter 数据后忘记调用 notifyDataSetChanged() → 界面不更新。
  • 错误: 将 RecyclerView 放在 Scrollable 容器内导致多次测量和滚动冲突。
  • 性能: Compose 的 key 参数与 RecyclerView 的 setHasStableIds 作用类似,必须为列表项提供唯一标识避免浪费重组/重新绑定。
  • 稳定性: 转换复杂动画时,优先使用 Compose 的 animateItemPlacement 而非 RecyclerView 的 ItemAnimator,避免 View 层与 Composable 动画冲突。
核心结论: RecyclerView 是“视图回收 + 命令式刷新”,Compose 是“声明式 UI + 智能重组”。混用时需要将 RecyclerView 视为“外部视图系统”,通过 AndroidView 包裹并同步生命周期,让两个体系互不干扰。