Fragment in Viewpager Not Restored After Popbackstack

Fragment in ViewPager not restored after popBackStack

After a lengthy investigation it turns out to be a problem with the fragment manager.

When using a construct like the one above the fragment transaction to reattach the fragment to the page list is silently discarded. It is basically the same problem that causes a

java.lang.IllegalStateException: Recursive entry to executePendingTransactions 

when trying to alter the fragments inside the FragmentPager.

The same solution, as for problems with this error, is also applicable here. When constructing the FragmentStatePagerAdapter supply the correct child fragment manager.

Instead of

    viewPager.setAdapter(new SimpleFragmentStatePagerAdapter(getFragmentManager(),mParentString));

do

    viewPager.setAdapter(new SimpleFragmentStatePagerAdapter(getChildFragmentManager(),mParentString));

See also: github

ViewPager Fragments are not recreated after FragmentTransaction.replace() followed by back button

After struggling with this for a few days I realized that the problem actually was that I passed the wrong FragmentManager to the ViewPager. It is indeed the FragmentManager returned by Fragment.getChildFragmentManager() that should be used. This makes sense, since it is the Fragment itself that stores the state of the child Fragments in the ViewPager. I am not sure why I couldn't make that work before, but now the LifeCycle of the app works fine with the following setup in my main Fragment's onCreate() method:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View view = inflater.inflate(R.layout.mm_main_fragment, container, false);

mMMActivity = (MMActivity) container.getContext();
mMMActivity.getSupportActionBar().setTitle(getString(R.string.mm_toolbar_title_main_fragment));

// Setup ViewPager.
mViewPager = (ViewPager) view.findViewById(R.id.mm_pager);
mAdapter = new MMPagerAdapter(getChildFragmentManager(), mMMActivity); // <-- This is the key
mViewPager.setAdapter(mAdapter);
mViewPager.setOffscreenPageLimit(PagerConstants.OFFSCREEN_PAGE_LIMIT);
mViewPager.setCurrentItem(PagerConstants.PAGE_FILTER_RECIPES);

// Setup TabLayout.
TabLayout tabLayout = (TabLayout) view.findViewById(R.id.mm_tablayout);
tabLayout.setupWithViewPager(mViewPager);

return view;
}

This was also the solution of another question. As a side note, I use this solution to get the Fragments in my ViewPager and it works just fine with the LifeCycle.

Test Application:

In the research of this issue I created a test application for this. The ones interested can clone it from here. It has ugly colors.

ViewPager holds fragment's instance even after setting a new adapter

I found the solution.

The solution is very simple. I just had to set manually null to the Child's adapter. With this, the ViewPager is forced to destroy every fragment.

So into the onDestroyView of Fragment's father I added:

@Override
public void onDestroyView() {
super.onDestroyView();
mViewpagerChild.removeOnPageChangeListener(mOnPaymentMethodsPageChangeListener);
mViewpagerChild.setAdapter(null); // <-- This is what I added
}

ViewPager swallows onBackPressed so FragmentContainerView cannot be navigated

As per the Fragment Manager guide:

Consider the navigation structure as a series of layers, with the activity as the outermost layer, wrapping each layer of child fragments underneath. Each layer must have a single primary navigation fragment. When the Back event occurs, the innermost layer controls navigation behavior. Once the innermost layer has no more fragment transactions from which to pop back, control returns to the next layer out, and this process repeats until you reach the activity.

So while app:defaultNavHost="true" is setting the NavHostFragment inside your FragmentA as the primary navigation fragment in its layer (the childFragmentManager layer of FragmentA), FragmentA must also be set as the primary navigation fragment in order for any system back button events to make it to FragmentA at all.

That same page brings up how to do that:

To define the primary navigation fragment inside of a fragment transaction, call the setPrimaryNavigationFragment() method on the transaction, passing in the instance of the fragment whose childFragmentManager should have primary control.

While ViewPager2 has APIs to allow you to set the primary navigation fragment to the current page, ViewPager1 has no such API (one of the many reasons why you should absolutely move to ViewPager2).

As a workaround for this in ViewPager1, FragmentA could set itself as the primary navigation fragment in onResume():

public fun onResume() {
super.onResume()
parentFragmentManager
.beginTransaction()
.setPrimaryNavigationFragment(this)
.commit()
}


Related Topics



Leave a reply



Submit