I have the following requirement - a vertical Recyclerview of List which contains a TextView and a child vertical RecyclerView of List. This data comes from an API. The issue is that the inner RV doesn't scroll and doesn't display all the items, only the first 4 or so. Ideally I want the child RV to display all the items. What's interesting is if I make the child RV horizontal, this problem goes away, I can fully scroll and see all the items.
I have the following implementation.
Parent
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 TitlesBinding.Holder(binding)
}
override fun onBindViewHolder(holder: Holder, position: Int) {
val itemRow = categoryList.get(position)
holder.bindRow(itemRow, articlesList)
}
override fun getItemCount(): Int {
return categoryList.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)
}
}
}
Child RV 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 RowArticleBinding.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
}
}
}
The Parent RV's xml looks like this:
<?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>
And the child RV's XML:
<?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>
At a guess it's because your parent list's items have a fixed height of match_parent
(i.e. whatever height the parent RecyclerView happens to occupy) while the child RV's height is wrap_content
, i.e. "as tall as necessary to display all its contents without scrolling". So the child RV will be displaying everything at once, but with all but the first 4 items off the bottom of the screen - you can use the Layout Inspector while your app is running to see what's going on.
With scrollable content (e.g. RecyclerView
s, ScrollView
s etc.) you need to limit that container's height to a "window" where you want to display content. If the content is bigger than that window, then it'll scroll. Generally wrap_content
on the axis you want to scroll is an error, because it will prevent it from scrolling! And you'll end up with a large view that's too big to be fully displayed.
So instead, the RecyclerView
in your parent RV file should have a constrained height, like 0dp
with layout_weight="1"
in a LinearLayout
so it fills the remaining available space in its parent layout. That way, if its contents are too big, it will remain at that fixed size and scroll instead of expanding.