[SOLVED] Android RecyclerView height problem inside view pager2 inside NestedScrollView with TabLayout


This Question and Answer are collected from stackoverflow and tested by JTuto community, is licensed under
CC BY-SA 2.5. - CC BY-SA 3.0. - CC BY-SA 4.0.

Issue

I have a problem. I want hiding TopLayout when view pager scrolling. I had been successfully this but recycler views heights are wrong when NestedScrollView scrolling.

HomeFragment.kt

Main fragment is here

    private var fragments = mutableListOf(FirstFragment(), SecondFragment())

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        tabLayout = view.findViewById(R.id.tabs)
        val viewPager2 = view.findViewById<ViewPager2>(R.id.view_pager)
        nsd = view.findViewById(R.id.nested_id)


        val adapter = ViewPagerAdapter(context as FragmentActivity, fragments)
        viewPager2.adapter = adapter

        TabLayoutMediator(
            tabLayout, viewPager2
        ) { tab, position ->
            tab.text = "Tab " + (position + 1)
        }.attach()
    }

home_fragment.xml


<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <androidx.core.widget.NestedScrollView
        android:id="@+id/nested_id"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <androidx.constraintlayout.widget.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <LinearLayout
                android:id="@+id/image"
                android:layout_width="match_parent"
                android:layout_height="150d"
                android:orientation="horizontal"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent" />

            <com.google.android.material.tabs.TabLayout
                android:id="@+id/tabs"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                app:layout_constraintTop_toBottomOf="@id/image" />

            <androidx.viewpager2.widget.ViewPager2
                android:id="@+id/view_pager"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintTop_toBottomOf="@id/tabs" />

        </androidx.constraintlayout.widget.ConstraintLayout>
    </androidx.core.widget.NestedScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>

FirstFragment.kt

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        lateinit var recyclerAdapter: MainRecyclerAdapter

        val cities = arrayListOf("cities1", "cities2","...","cities81")

        var recyclerView: RecyclerView = view.findViewById(R.id.fr_recyclerView)

        val gr = GridLayoutManager(context, 2, GridLayoutManager.VERTICAL, false)
        recyclerView.layoutManager = gr
        recyclerAdapter = MainRecyclerAdapter(cities)
        recyclerView.adapter = recyclerAdapter
    }

fragment_first.xml

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".FirstFragment">

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

</FrameLayout>

SecondFragment.kt

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        lateinit var recyclerAdapter: MainRecyclerAdapter

        val cities = arrayListOf("cities1", "cities2","...","cities10")

        var recyclerView: RecyclerView = view.findViewById(R.id.fr_recyclerView)

        val gr = GridLayoutManager(context, 2, GridLayoutManager.VERTICAL, false)
        recyclerView.layoutManager = gr
        recyclerAdapter = MainRecyclerAdapter(cities)
        recyclerView.adapter = recyclerAdapter
    }

fragment_second.xml

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#1F4A8A"
    tools:context=".SecondFragment">
    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/second_fr_recyclerView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>
</FrameLayout>

RecyclerViewAdapter.kt

class MainRecyclerAdapter(private val list: ArrayList<String>) :
    RecyclerView.Adapter<MainRecyclerAdapter.ViewHolder>() {

    class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        var city: TextView

        init {
            city = itemView.findViewById(R.id.text)
        }
    }

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

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.city.text = list[position]
    }

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

Solution

The solution is to register a PageChangeCallback and adjust the LayoutParams of the ViewPager2 after asking the child to re-measure itself.

pager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
    override fun onPageSelected(position: Int) {
        super.onPageSelected(position)
        val view = // fragment view
        view.post {
            val wMeasureSpec = MeasureSpec.makeMeasureSpec(view.width, MeasureSpec.EXACTLY)
            val hMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)
            view.measure(wMeasureSpec, hMeasureSpec)

            if (pager.layoutParams.height != view.measuredHeight) {
                pager.layoutParams = (pager.layoutParams as ConstraintLayout.LayoutParams)
                    .also { lp -> lp.height = view.measuredHeight }
            }
        }
    }
})

Answered By – yakupcann5

people found this article helpful. What about you?