You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在ListAdapter<T, VH>中实现嵌套RecyclerView

使用ListAdapter实现嵌套RecyclerView完全可行

ListAdapter是RecyclerView.Adapter的子类,天然支持嵌套场景,核心是分别为父、子RecyclerView实现对应的ListAdapter,并在父ViewHolder中完成子RecyclerView的配置与数据绑定。以下是具体实现步骤:

1. 定义嵌套数据结构

先明确父项和子项的数据模型:

// 子项数据类,存储单个子项内容
data class ChildItem(val name: String)

// 父项数据类,包含父项标题和对应的子项列表
data class ParentItem(val title: String, val childItems: List<ChildItem>)

2. 实现子ListAdapter

负责渲染父项中嵌套的子RecyclerView,核心是实现DiffCallback以支持高效数据更新:

class ChildAdapter : ListAdapter<ChildItem, ChildAdapter.ChildViewHolder>(DiffCallback()) {

    inner class ChildViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        private val tvChildName: TextView = itemView.findViewById(R.id.child_item_name) // 替换为你的子项布局中的TextView ID

        fun bind(childItem: ChildItem) {
            tvChildName.text = childItem.name
        }
    }

    // 实现DiffCallback判断数据变化
    class DiffCallback : DiffUtil.ItemCallback<ChildItem>() {
        override fun areItemsTheSame(oldItem: ChildItem, newItem: ChildItem): Boolean {
            // 根据业务唯一标识判断是否为同一子项
            return oldItem.name == newItem.name
        }

        override fun areContentsTheSame(oldItem: ChildItem, newItem: ChildItem): Boolean {
            // 判断内容是否一致
            return oldItem == newItem
        }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ChildViewHolder {
        val view = LayoutInflater.from(parent.context)
            .inflate(R.layout.item_child, parent, false) // 替换为你的子项布局文件
        return ChildViewHolder(view)
    }

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

3. 实现父ListAdapter

父Adapter需要在ViewHolder中初始化子RecyclerView,并绑定子Adapter的数据:

class ParentAdapter : ListAdapter<ParentItem, ParentAdapter.ParentViewHolder>(DiffCallback()) {

    inner class ParentViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        private val tvParentTitle: TextView = itemView.findViewById(R.id.content_title)
        private val childRecyclerView: RecyclerView = itemView.findViewById(R.id.child_recycler_view)
        private val childAdapter = ChildAdapter()

        init {
            // 配置子RecyclerView的布局管理器(示例为横向布局,可根据需求改为纵向)
            childRecyclerView.layoutManager = LinearLayoutManager(itemView.context, LinearLayoutManager.HORIZONTAL, false)
            childRecyclerView.adapter = childAdapter
            // 禁用子RecyclerView的嵌套滑动,适配外层NestedScrollView
            childRecyclerView.isNestedScrollingEnabled = false
        }

        fun bind(parentItem: ParentItem) {
            tvParentTitle.text = parentItem.title
            // 提交子项数据到子Adapter
            childAdapter.submitList(parentItem.childItems)
        }
    }

    // 父项的DiffCallback实现
    class DiffCallback : DiffUtil.ItemCallback<ParentItem>() {
        override fun areItemsTheSame(oldItem: ParentItem, newItem: ParentItem): Boolean {
            return oldItem.title == newItem.title
        }

        override fun areContentsTheSame(oldItem: ParentItem, newItem: ParentItem): Boolean {
            return oldItem == newItem
        }
    }

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

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

4. 在主Activity中配置父RecyclerView

完成父RecyclerView的布局管理器、Adapter绑定,并提交测试数据:

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

        val parentRecyclerView = findViewById<RecyclerView>(R.id.parent_recycler_view)
        val parentAdapter = ParentAdapter()

        // 配置父RecyclerView的布局管理器
        parentRecyclerView.layoutManager = LinearLayoutManager(this)
        parentRecyclerView.adapter = parentAdapter
        // 禁用父RecyclerView的嵌套滑动,避免与外层NestedScrollView冲突
        parentRecyclerView.isNestedScrollingEnabled = false

        // 构造测试数据
        val testData = listOf(
            ParentItem(
                title = "House of Stark",
                childItems = listOf(ChildItem("Robb"), ChildItem("Sansa"), ChildItem("Arya"), ChildItem("Bran"), ChildItem("Rickon"))
            ),
            ParentItem(
                title = "House of Lannister",
                childItems = listOf(ChildItem("Tywin"), ChildItem("Cersei"), ChildItem("Jaime"), ChildItem("Tyrion"))
            )
        )

        // 提交数据到父Adapter
        parentAdapter.submitList(testData)
    }
}

关键注意点

  • DiffCallback正确性:areItemsTheSame要基于数据的唯一标识判断,areContentsTheSame要确保内容完全一致,否则ListAdapter无法正确更新UI。
  • 嵌套滑动优化:外层使用NestedScrollView时,必须将父、子RecyclerView的isNestedScrollingEnabled设为false,否则会出现滑动卡顿或滚动冲突。
  • Adapter复用:子Adapter在父ViewHolder的init块中初始化,不要在bind方法中重复创建,避免不必要的性能开销。

内容的提问来源于stack exchange,提问作者Abhishek Charismatic

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 13:21:24