Search code examples
androidandroid-recyclerviewnestedrecyclerview

Nested RecyclerView Adding Space Between Items After First Scroll


I am trying to implement a simple nested recyclerview with a vertical parent and horizontal children.

The layout for the parent is:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/parentLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="10dp">

    <include layout="@layout/loading_indicator" />

    <include layout="@layout/error_layout" />

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/podcasts"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/podcastsTitle"
        android:layout_marginTop="5dp" />
</RelativeLayout>

The layout for the child recyclerview is:

<?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:layout_marginBottom="10dp"
    android:orientation="vertical">

    <TextView
        android:id="@+id/title"
        style="@style/TitleTextStyle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="3dp"
        android:textAppearance="@style/TextAppearance.AppCompat.Large" />

    <TextView
        android:id="@+id/description"
        style="@style/AuxTextStyle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="5dp"
        android:maxLines="2"
        android:textAppearance="@style/TextAppearance.AppCompat.Medium" />

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/podcastsItems"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</LinearLayout>

The adapter for the parent is as follows:

class CuratedPodcastsAdapter(val context: Context, private val podcastLists: ArrayList<PodcastList>) : RecyclerView.Adapter<CuratedPodcastsAdapter.ViewHolder>() {

    private val viewPool = RecyclerView.RecycledViewPool()

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder = ViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.podcast_section_layout, parent, false))

    override fun getItemCount(): Int = podcastLists.size

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.bindItems(podcastLists[holder.adapterPosition])
    }

    inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {

        @BindView(R.id.title)
        lateinit var title: TextView
        @BindView(R.id.description)
        lateinit var description: TextView
        @BindView(R.id.podcastsItems)
        lateinit var podcastItems: RecyclerView

        init {
            ButterKnife.bind(this, itemView)
        }

        fun bindItems(curatedPodcastList: PodcastList) {
            title.text = curatedPodcastList.title
            description.text = curatedPodcastList.description

            title.typeface = FontUtils.boldTypeface
            description.typeface = FontUtils.mediumTypeface

            val childLayoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
            childLayoutManager.initialPrefetchItemCount = 4

            podcastItems.apply {
                layoutManager = childLayoutManager
                adapter = PodcastAdapter(this@CuratedPodcastsAdapter.context, curatedPodcastList.podcasts!!)
                setItemViewCacheSize(20)
                setRecycledViewPool(viewPool)
            }
        }
    }
}

while the children's adapter is:

class PodcastAdapter(val context: Context, private val episodes: ArrayList<Podcast>): RecyclerView.Adapter<PodcastAdapter.ViewHolder>() {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder = ViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.podcast_item_layout, parent, false))

    override fun getItemCount(): Int = episodes.size

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.bindItems(episodes[holder.adapterPosition])
    }

    inner class ViewHolder(view: View): RecyclerView.ViewHolder(view) {

        @BindView(R.id.art)
        lateinit var art: ImageView
        @BindView(R.id.title)
        lateinit var title: TextView
        @BindView(R.id.publisher)
        lateinit var publisher: TextView
        @BindView(R.id.card)
        lateinit var card: CardView

        init {
            ButterKnife.bind(this, view)
        }

        fun bindItems(podcast: Podcast){
            title.text = podcast.title
            publisher.text = podcast.publisher
            GlideApp.with(context).load(podcast.image).error(R.drawable.no_cover).into(art)

            card.setOnClickListener {
                context.startActivity(context.intentFor<PodcastActivity>(PodcastActivity.PODCAST_ID to podcast.id, PodcastActivity.PODCAST_TITLE to podcast.title))
            }
        }
    }
}

When content is loaded for the first time, everything appears correctly like below:

initial state

After scrolling downwards then back up again, spaces begin to appear between the content like so:

state after scroll

None of the recyclerviews have any item decoration added to them. My perception was that using a common viewpool would at least improve performance and help with such layout inconsistencies but it hasn't helped.

What is the cause of this problem and how can it be fixed?


Solution

  • You need to change the height of your child layout of recyclerview to android:layout_height="wrap_content"

    Sample code

    <?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="wrap_content"
        android:layout_marginBottom="10dp"
        android:orientation="vertical">
    
        <TextView
            android:id="@+id/title"
            style="@style/TitleTextStyle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginBottom="3dp"
            android:textAppearance="@style/TextAppearance.AppCompat.Large" />
    
        <TextView
            android:id="@+id/description"
            style="@style/AuxTextStyle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginBottom="5dp"
            android:maxLines="2"
            android:textAppearance="@style/TextAppearance.AppCompat.Medium" />
    
        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/podcastsItems"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
    
    </LinearLayout>