I have a Viewpager2
inside a Fragment
(lets call it HomeFragment
). That Viewpager
itself also contains Fragments
. When I navigate away from the HomeFragment
its view will be destroyed and when I navigate back the view will be recreated. Now I set the adapter of the Viewpager2
in the HomeFragment
during onViewCreated()
. Therefore the adapter will be recreated when I navigate back to the HomeFragment
, which also recreates all Fragments
in the Viewpager2
and the current item is reset to 0. If i try to re-use the adapter that I instantiated on the first creation of the HomeFragment
i get an exception, because of this check inside of the FragmentStateAdapter
:
public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) { checkArgument(mFragmentMaxLifecycleEnforcer == null);
Does anybody have an idea how I can prevent recreating everything when navigating back? Otherwise this is a pretty big performance overhead and hinders my UX.
I've spent a bit of time with this, and I've diagnosed the problem for anyone who needs to hear it. I tried to keep my solution as conventional as possible. If we look at your statement:
Therefore the adapter will be recreated when I navigate back to the HomeFragment, which also recreates all Fragments in the Viewpager2 and the current item is reset to 0.
The problem is that the current item is reset to 0, because the list that your adapter is based off-of is recreated. To resolve the issue, we don't need to save the adapter, just the data inside of it. With that in mind, solving the problem is not difficult at all.
Let's layout some definitions:
HomeFragment
is, as you've said, the host of your ViewPager2
,MainActivity
is the running activity which hosts HomeFragment
and all created fragments inside of itMyFragment
. You could even have more than one type of fragment that you page through, but that's beyond the scope of this example.PagerAdapter
is your FragmentStateAdapter
, which is the adapter for HomeFragment
's ViewPager2
.In this example, MyFragment
has the constructor constructor(id : Int)
. Then, PagerAdapter
is probably going to appear as follows:
class PagerAdapter(fm : Fragment) : FragmentStateAdapter(fm){ var ids : List<Int> = listOf() ... override fun createFragment(position : Int) : Fragment{ return MyFragment(ids[position]) } }
The problem that we are facing is every time you recreate PagerAdapter
the constructor is called and that constructor, as we can see above, sets ids
to an empty list.
My first thought was that maybe I could switch fm
to be MainActivity
. I don't navigate out of MainActivity
so I'm not sure why, but this solution doesn't work.
Instead, what you need to do is abstract the data out of PagerAdapter
. Create a "viewModel":
/* We do NOT extend ViewModel. This naming just indicates that this is your data- storage vehicle for PagerAdapter*/ data class PagerAdapterViewModel( var ids : List<Int> )
Then, in PagerAdapter
, make the following adjustments:
class PagerAdapter( fm : Fragment, private val viewModel : PagerAdapterViewModel ) : FragmentStateAdapter(fm){ // by creating custom getters and setters, you are migrating your code to this // implementation without needing to adjust any code outside of the adapter var ids : List<Int> get() = viewModel.ids set(value) {viewModel.ids = value} override fun createFragment(position : Int) : Fragment{ return MyFragment(ids[position]) } }
Finally, in HomeFragment
, you'll have something like:
class HomeFragment : Fragment(){ ... /** Calling "by lazy" ensures that this object is only created once, and hence we retain the data stored in it, even when navigating away. */ private val pagerAdapterViewModel : PagerAdapterViewModel by lazy{ PagerAdapterViewModel(listOf()) } private lateinit var pagerAdapter : PagerAdapter ... override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) ... pagerAdapter = PagerAdapter(this, pagerAdapterViewModel) pager.adapter = pagerAdapter ... } ... }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With