Home > Back-end >  ViewPager2 doesn't switch fragments
ViewPager2 doesn't switch fragments

Time:11-03

I have an app that uses NavigationComponents among with ViewPager 2.

I'd like to use ViewPager for switching between fragments. I've made my ActivityMain as FragmentContainerView, and I'd like to have ViewPager implemented in one of my fragments.

The problem is, the ViewPager doesn't work at all. It doesn't change fragments, don't know why. What should I change in the code?

ActivityMain

   <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
    <androidx.fragment.app.FragmentContainerView
        android:id="@ id/fragmentContainerView"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:defaultNavHost="true"
        app:navGraph="@navigation/nav_graph" />
</androidx.constraintlayout.widget.ConstraintLayout>

First fragment

    class BlankFragment : Fragment() {
    private var mPag: ViewPager2? = null


    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragment_blank, container, false)
    }

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

        mPag = view.findViewById(R.id.pager123)
        val adapter = PagerAdapter(this)
        val list = mutableListOf<Fragment>()
        list.add(BlankFragment())
        list.add(BlankFragment2())
        mPag?.adapter = adapter
    }
}

.xml

  <?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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=".BlankFragment"
    android:orientation="vertical">
    <TextView
        android:id="@ id/test123"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="111111111" />

    <androidx.viewpager2.widget.ViewPager2
        android:id="@ id/pager123"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#a1a1"/>
</LinearLayout>

Second fragment

    class BlankFragment2 : Fragment() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

    }
    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_blank2, container, false)
    }
}

.xml

   <?xml version="1.0" encoding="utf-8"?>
<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=".BlankFragment2">
    <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:text="222222222222222" />

</FrameLayout>

Nav graph

 <?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@ id/nav_graph"
    app:startDestination="@id/blankFragment">

    <fragment
        android:id="@ id/blankFragment"
        android:name="com.example.myapplication.BlankFragment"
        android:label="fragment_blank"
        tools:layout="@layout/fragment_blank" />
    <fragment
        android:id="@ id/blankFragment2"
        android:name="com.example.myapplication.BlankFragment2"
        android:label="fragment_blank2"
        tools:layout="@layout/fragment_blank2" />
</navigation>

Pager adapter

   class PagerAdapter(fragment: Fragment) : FragmentStateAdapter(fragment) {
    val mFragments = mutableListOf<Fragment>()
    
    override fun getItemCount(): Int {
        return mFragments.size
    }

    override fun createFragment(position: Int): Fragment {
        when (position){
            0 -> return BlankFragment()
            1 -> return BlankFragment2()
        }
        return mFragments[position]
    }
}

CodePudding user response:

What you are essentially missing is what I tried to explain in the previous question.

You need to use Navigation component and ViewPager2 as two separate entities.

There is also issue with your PagerAdapter. Fragment adapters are not similar to other Adapters you might have experience with(mFragments). They shouldn't hold a reference to those fragments. There are two main Fragment adapters(more here).

class PagerAdapter(fragment: Fragment) : FragmentStateAdapter(fragment) {

    override fun getItemCount(): Int = 2

    override fun createFragment(position: Int): Fragment {
        return when (position){
            0 -> BlankFragment1()
            1 -> BlankFragment2()
            else -> throw IllegalArgumentException("Out of fragments, will depend on getItemCount")
        }
    }
}

You need to have Fragments that are solely in the Navigation component(e.g. FirstFragment & SecondFragment) and Fragments that belong to the ViewPager2(e.g. BlankFragment1 & BlankFragment2)

<?xml version="1.0" encoding="utf-8"?>
<navigation android:id="@ id/nav_graph"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    app:startDestination="@id/FirstFragment">

    <fragment
        android:id="@ id/FirstFragment"
        android:name="com.example.myapplication.FirstFragment"
        android:label="@string/first_fragment_label"
        tools:layout="@layout/fragment_first" >
        <action
            android:id="@ id/action_FirstFragment_to_SecondFragment"
            app:destination="@id/SecondFragment" />
    </fragment>

    <fragment
        android:id="@ id/SecondFragment"
        android:name="com.example.myapplication.SecondFragment"
        android:label="@string/second_fragment_label"
        tools:layout="@layout/fragment_second" />
</navigation>

Fragments for ViewPager2:

class BlankFragment1: Fragment() {
    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        return inflater.inflate(R.layout.fragment_blank_1, container, false)
    }
}
class BlankFragment2: Fragment() {
    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        return inflater.inflate(R.layout.fragment_blank_2, container, false)
    }
}

The FirstFragment is going to host the ViewPager2 that will allow you to swipe between BlankFragment1 and BlankFragment2.

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,savedInstanceState: Bundle?): View? {
    val view = inflater.inflate(R.layout.fragment_first, container, false)

    view.findViewById<ViewPager2>(R.id.vp2)?.let { viewPager2 ->
        val pagerAdapter = PagerAdapter(this)
        viewPager2.adapter = pagerAdapter
    }

    return view
}

Now the only thing you need to do in any of the fragments to use Navigation component is to simply findNavControler and navigate to destination you want.

class BlankFragment1: Fragment() {
    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        val view =  inflater.inflate(R.layout.fragment_blank_1, container, false)

        view.findViewById<MaterialButton>(R.id.blank_1_button).setOnClickListener {
            findNavController().navigate(R.id.action_FirstFragment_to_SecondFragment)
        }
        return view
    }
}
  • Related