Search code examples
androidkotlinandroid-recyclerviewlinearlayoutmanager

Prefetching view for child recycler view is not working


I have vertical RecyclerView with child(nested) horizontal RecyclerView. For second recycler I use layout manager:

LinearLayoutManager(itemView.context, LinearLayoutManager.HORIZONTAL, false).apply {
                    isItemPrefetchEnabled = true
                    initialPrefetchItemCount = 4
                }

But nested recycler builds only first two visible items.

Full code:

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val recycler = findViewById<RecyclerView>(R.id.recycler)

        recycler.layoutManager = LinearLayoutManager(this)
        recycler.adapter = MainAdapter(buildElements())
    }

    private fun buildElements(): List<Parent> {
        return (0..2).map { pCount -> createParent(pCount) }
    }

    private fun createParent(index: Int): Parent {
        return Parent(text = "$index",
            list = (0..30).map { Child("item $it") }
        )
    }
}

Main adapter

class MainAdapter(private val list: List<Parent>) : RecyclerView.Adapter<MainAdapter.CustomHolder>() {

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

    override fun getItemCount(): Int = list.size

    override fun onBindViewHolder(holder: CustomHolder, position: Int) {
        holder.bind(list[position])
    }

    class CustomHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        private val recycler = itemView.findViewById<RecyclerView>(R.id.recyclerView)
        private val title = itemView.findViewById<TextView>(R.id.title)

        init {
            recycler.layoutManager =
                LinearLayoutManager(itemView.context, LinearLayoutManager.HORIZONTAL, false).apply {
                    isItemPrefetchEnabled = true
                    initialPrefetchItemCount = 4
                }
        }

        fun bind(data: Parent) {
            title.text = data.text
            recycler.adapter = ChildAdapter(data.list)
        }
    }
}

Main recycler element

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

    <TextView
            android:id="@+id/title"
            android:layout_width="wrap_content"
            android:layout_height="16dp"/>

    <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/recyclerView"
            android:layout_marginTop="16dp"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>
</FrameLayout>

Child adapter

class ChildAdapter(private val list: List<Child>) : RecyclerView.Adapter<ChildAdapter.ChildHolder>() {

    companion object {
        private val TAG = ChildAdapter::class.java.simpleName
    }

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

    override fun getItemCount(): Int = list.size

    override fun onBindViewHolder(holder: ChildHolder, position: Int) {
        holder.bind(list[position])
    }

    class ChildHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        private val title = itemView.findViewById<TextView>(R.id.childText)
        fun bind(data: Child) {
            title.text = data.text
            Log.d(TAG, "bind child element: ${data.text}")
        }
    }
}

And child element:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="275dp"
        android:padding="8dp"
        android:layout_height="100dp">

    <TextView
            android:id="@+id/childText"
            android:layout_width="wrap_content"
            android:layout_gravity="center"
            android:layout_height="wrap_content"/>
</FrameLayout>

In logs is see only this:

D/ChildAdapter: bind child element: item 0
D/ChildAdapter: bind child element: item 1
D/ChildAdapter: bind child element: item 0
D/ChildAdapter: bind child element: item 1
D/ChildAdapter: bind child element: item 0
D/ChildAdapter: bind child element: item 1

Logs mean that only the first and second elements was build, but I want prebuilt also third and forth elements.

Also I try use this, but it also not working


Solution

  • prefetching working only for scrolling, not for the first initial items creation.

    Finally I use custom layout manager with override getExtraLayoutSpace and return 100000. My solution potentially dangerous, but I use it, because I know that item count can't be more than 10 items.