ViewPager2 with TabLayout not navigating to correct tab

Viewed 3106

My TabLayout (containing 5 tabs) doesn't seem to behave with ViewPager2 properly for some reasomn. The app loads fine but when I click Tab D, it goes to Tab E instead. Why does that happen and what can be done to fix this?

Activity

Fragment

class MyFragment : androidx.fragment.app.Fragment() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    }

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        val v = inflater.inflate(R.layout.my_fragment, container, false)
        super.onCreate(savedInstanceState)

        return v
    }

    override fun onActivityCreated(savedInstanceState: Bundle?) {

        // Set TabLayout tab names
        mViewPager2!!.adapter = MyAdapter(fragmentManager, lifecycle)
        TabLayoutMediator(mTabLayout, mViewPager2, TabLayoutMediator.TabConfigurationStrategy{ tab, position ->
            when (position) {
                0 -> tab.text = getString(R.string.a)
                1 -> tab.text = getString(R.string.b)
                2 -> tab.text = getString(R.string.c)
                3 -> tab.text = getString(R.string.d)
                4 -> tab.text = getString(R.string.e)
            }
        }).attach()

        super.onActivityCreated(savedInstanceState)
    }

    // Set TabLayout tab classes
    private inner class MyAdapter(fm: FragmentManager?, lifecycle: Lifecycle) : FragmentStateAdapter(fm!!, lifecycle) {
        private val intItems = 5

        override fun createFragment(position: Int): Fragment {
            var fragment: Fragment? = null
            when (position) {
                0 -> fragment = FragmentA()
                1 -> fragment = FragmentB()
                2 -> fragment = FragmentC()
                3 -> fragment = FragmentD()
                4 -> fragment = FragmentE()
            }
            return fragment!!
        }

        override fun getItemCount(): Int {
            return intItems
        }
    }
}

my_fragment.xml (ViewPager2 with Tabs)

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

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

    <com.google.android.material.tabs.TabLayout
            android:id="@+id/myTabLayout"
            android:layout_width="match_parent"
            android:layout_height="?android:attr/actionBarSize"
            app:tabMode="scrollable"
            app:tabTextAppearance="@android:style/TextAppearance.Widget.TabWidget" />

    <androidx.viewpager2.widget.ViewPager2
            android:id="@+id/mViewPager2"
            android:layout_height="0dp"
            android:layout_width="match_parent"
            android:layout_weight="1" />
</LinearLayout>
2 Answers

My thoughts are that you are using the wrong fragmentManager the tutorial you linked creates the viewpager2 in an Activity where as you are creating it in a Fragment and possibly using the fragmentManager that already has a fragment "MyFragment" associated with it.

In my answer https://stackoverflow.com/a/60961499/2373819 to your other question it links to the source code of the other constructor of viewpager2 for a Fragment uses the getChildFragmentManager

which is

Return a private FragmentManager for placing and managing Fragments inside of this Fragment.

Therefore because I don't think you are using the right fragmentManager the extra parent fragment is causing this off by one bug.

I suggest you either construct you adapter giving it the current fragment like

mViewPager2!!.adapter = MyAdapter(this)

or

 mViewPager2!!.adapter = MyAdapter(this.getChildFragmentManager(), this.getLifecycle())

Sorry my kotlin is not great, but hopefully you get the idea (basic the two options are the same and the first is probably the best to use)

Ummm... Make sure to use this TabLayoutMediator as following after you set adapter to ViewPager2

TabLayoutMediator(tab_layout, view_pager) { tab, position ->
                tab.text = fragmentList[position].second
            }.attach()
Related