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

嵌套垂直RecyclerView无法显示全部内容,改为水平则正常

问题描述

实现一个垂直RecyclerView,其列表项包含一个TextView和一个垂直子RecyclerView,数据来源于API。目前遇到的问题是:子RecyclerView无法滚动,且仅能显示前4条左右的内容;但将子RecyclerView设置为水平方向时,该问题消失,可正常滚动并查看所有条目。

父RecyclerView Adapter代码

class TitlesAdapter(
    private val titlesList: List<Title>,
    private val articlesList: List<Article>
): RecyclerView.Adapter<TitlesAdapter.Holder>() {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
        val binding = TitlesBinding.inflate(LayoutInflater.from(parent.context),parent,false)
        return Holder(binding)
    }

    override fun onBindViewHolder(holder: Holder, position: Int) {
        val itemRow = titlesList.get(position)
        holder.bindRow(itemRow, articlesList)
    }

    override fun getItemCount(): Int {
        return titlesList.size
    }

    class Holder(var binding: TitlesBinding): RecyclerView.ViewHolder(binding.root){

        fun bindRow(
            title: Title,
            articles: List<Article>
        ) {
            binding.tvTitleName.text = title.name
            val filteredList = articles.filter { it.category == title.id }

            binding.rvChild.adapter = ChildRVAdapter(filteredList)
            binding.rvChild.layoutManager = LinearLayoutManager(itemView.context)
        }
    }
}

子RecyclerView Adapter代码

class ChildRVAdapter(private val articleList: List<Article>): RecyclerView.Adapter<ChildRVAdapter.Holder>() {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
        val binding = RowArticleBinding.inflate(LayoutInflater.from(parent.context),parent,false)
        return Holder(binding)
    }

    override fun onBindViewHolder(holder: Holder, position: Int) {
        val itemRow = articleList.get(position)
        holder.bindRow(itemRow)
    }

    override fun getItemCount(): Int {
        return articleList.size
    }

    class Holder(var binding: RowArticleBinding): RecyclerView.ViewHolder(binding.root), View.OnClickListener{

        fun bindRow(
            article: Article
        ) {
            binding.tvTitle.text = article.title
            binding.tvDesc.text = article.description
        }
    }
}

父RecyclerView列表项布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <TextView
        android:id="@+id/tv_title_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="20sp"
        android:textColor="@color/textBlack"
        android:fontFamily="@font/playfair_bold"
        android:layout_marginBottom="18dp"
        android:text="Introduction"/>
    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/rv_child"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>
</LinearLayout>

子RecyclerView列表项布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <TextView
        android:id="@+id/tv_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Introduction from Annabel Karmel"
        android:textColor="@color/textBlack"
        android:textSize="14sp"
        android:fontFamily="@font/montserrat_bold"/>
    <TextView
        android:id="@+id/tv_desc"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="@color/dustyGrey"
        android:layout_marginTop="6dp"
        android:textSize="14sp"
        android:fontFamily="@font/montserrat_medium"/>
</LinearLayout>
解决方案

问题根源

垂直嵌套RecyclerView时,父RecyclerView的默认LayoutManager无法准确计算子RecyclerView的实际高度(子RecyclerView设为wrap_content),导致子RecyclerView被限制高度,无法滚动且仅显示部分内容;而水平方向时,宽度计算不受父垂直RecyclerView的布局逻辑影响,因此能正常显示和滚动。


方案1:自定义LayoutManager让子RecyclerView正确测量高度

创建自定义LinearLayoutManager,强制计算所有子项的总高度,确保子RecyclerView能完整展示内容:

class WrapContentLinearLayoutManager(context: Context) : LinearLayoutManager(context) {
    override fun onMeasure(recycler: RecyclerView.Recycler, state: RecyclerView.State, widthSpec: Int, heightSpec: Int) {
        var totalHeight = 0
        val widthSize = View.MeasureSpec.getSize(widthSpec)

        // 遍历所有子项计算总高度
        for (i in 0 until itemCount) {
            val childView = recycler.getViewForPosition(i)
            measureChild(childView, widthSpec, heightSpec)
            totalHeight += childView.measuredHeight
            recycler.recycleView(childView)
        }

        // 重新构造高度测量参数
        val newHeightSpec = if (View.MeasureSpec.getMode(heightSpec) == View.MeasureSpec.EXACTLY) {
            heightSpec
        } else {
            View.MeasureSpec.makeMeasureSpec(totalHeight, View.MeasureSpec.EXACTLY)
        }

        super.onMeasure(recycler, state, widthSpec, newHeightSpec)
    }
}

然后在父Adapter的Holder中替换原LayoutManager:

// 在bindRow方法中修改
binding.rvChild.layoutManager = WrapContentLinearLayoutManager(itemView.context)

方案2:禁用嵌套滚动+优化Adapter复用

  1. 在子RecyclerView的布局中添加禁用嵌套滚动的属性:
<androidx.recyclerview.widget.RecyclerView
    android:id="@+id/rv_child"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:nestedScrollingEnabled="false"/>
  1. 优化父Adapter,避免每次绑定都创建新的子Adapter和LayoutManager,提升性能同时解决测量问题:
class TitlesAdapter(
    private val titlesList: List<Title>,
    private val articlesList: List<Article>
): RecyclerView.Adapter<TitlesAdapter.Holder>() {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
        val binding = TitlesBinding.inflate(LayoutInflater.from(parent.context),parent,false)
        return Holder(binding)
    }

    override fun onBindViewHolder(holder: Holder, position: Int) {
        val itemRow = titlesList[position]
        holder.bindRow(itemRow, articlesList)
    }

    override fun getItemCount(): Int {
        return titlesList.size
    }

    class Holder(var binding: TitlesBinding): RecyclerView.ViewHolder(binding.root){
        private val childAdapter = ChildRVAdapter(emptyList())

        init {
            // 初始化时就设置LayoutManager和Adapter,避免重复创建
            binding.rvChild.layoutManager = LinearLayoutManager(itemView.context)
            binding.rvChild.adapter = childAdapter
            binding.rvChild.isNestedScrollingEnabled = false
        }

        fun bindRow(
            title: Title,
            articles: List<Article>
        ) {
            binding.tvTitleName.text = title.name
            val filteredList = articles.filter { it.category == title.id }
            // 更新子Adapter数据而非创建新Adapter
            childAdapter.updateData(filteredList)
        }
    }
}

// 给子Adapter添加数据更新方法
class ChildRVAdapter(private var articleList: List<Article>): RecyclerView.Adapter<ChildRVAdapter.Holder>() {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
        val binding = RowArticleBinding.inflate(LayoutInflater.from(parent.context),parent,false)
        return Holder(binding)
    }

    override fun onBindViewHolder(holder: Holder, position: Int) {
        val itemRow = articleList[position]
        holder.bindRow(itemRow)
    }

    override fun getItemCount(): Int {
        return articleList.size
    }

    // 添加数据更新方法
    fun updateData(newList: List<Article>) {
        articleList = newList
        notifyDataSetChanged()
    }

    class Holder(var binding: RowArticleBinding): RecyclerView.ViewHolder(binding.root), View.OnClickListener{

        fun bindRow(
            article: Article
        ) {
            binding.tvTitle.text = article.title
            binding.tvDesc.text = article.description
        }
    }
}

方案3:使用单一RecyclerView+多类型布局(推荐)

嵌套RecyclerView容易带来性能问题和布局冲突,推荐使用单一RecyclerView,通过多类型Item实现相同效果:

  • 定义两种ItemType:标题类型、文章类型
  • 在Adapter中根据位置判断ItemType,加载对应的布局和数据
  • 这种方式避免了嵌套,性能更优且无布局测量问题

内容的提问来源于stack exchange,提问作者Alex Petev

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 22:07:23