Fragment in ViewPager not restored after popBackStack

后端 未结 4 605
死守一世寂寞
死守一世寂寞 2020-12-01 01:57

Problem

A Fragment is not reattached to its hosting ViewPager after returning from another fragment.

Situation

One Activity hosting a Fragment wh

相关标签:
4条回答
  • 2020-12-01 02:21

    Delete all page fragments, enabling them to be re-added later

    The page fragments are not attached when you return to the viewpager screen as the FragmentStatePagerAdapter is not re-connecting them. As a work-around, delete all the fragments in the viewpager after popbackstack() is called, which will allow them to be re-added by your initial code.

    [This example is written in Kotlin]

    //Clear all fragments from the adapter before they are re-added.
    for (i: Int in 0 until adapter.count) {
        val item = childFragmentManager.findFragmentByTag("f$i")
        if (item != null) {
            adapter.destroyItem(container!!, i, item)
        }
    }
    
    0 讨论(0)
  • 2020-12-01 02:24

    What Paul has failed to mention is, if you use getChildFragmentManager, then you will suffer the "blank screen on back pressed" issue.

    0 讨论(0)
  • 2020-12-01 02:26

    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

    0 讨论(0)
  • 2020-12-01 02:31

    The hierarchy in my case was:

    MainActivity->MainFragment->TabLayout+ViewPager->AccountsFragment+SavingsFragment+InvestmentsFragment etc.

    The problem I had was that I couldn't use childFragmentManagerfor the reason that a click on the item Account view (who resides inside one of the Fragments of the ViewPager) needed to replace MainFragment i.e. the entire screen.

    Using MainFragments host Fragment i.e. passing getFragmentManager() enabled the replacing, BUT when popping the back-stack, I ended up with this screen:

    This was apparent also by looking at the layout inspector where the ViewPager is empty.

    Apparently looking at the restored Fragments you would notice that their View is restored but will not match the hierarchy of the popped state. In order to make the minimum impact and not force a re-creation of the Fragments I re-wrote FragmentStatePagerAdapter with the following changes:

    I copied the entire code of FragmentStatePagerAdapter and changed

    @NonNull
    @Override
    public Object instantiateItem(@NonNull ViewGroup container, int position) {
        // If we already have this item instantiated, there is nothing
        // to do.  This can happen when we are restoring the entire pager
        // from its saved state, where the fragment manager has already
        // taken care of restoring the fragments we previously had instantiated.
        if (mFragments.size() > position) {
            Fragment f = mFragments.get(position);
            if (f != null) {
                return f;
            }
        }
    ...
    }
    

    with

    @NonNull
    @Override
    public Object instantiateItem(@NonNull ViewGroup container, int position) {
        // If we already have this item instantiated, there is nothing
        // to do.  This can happen when we are restoring the entire pager
        // from its saved state, where the fragment manager has already
        // taken care of restoring the fragments we previously had instantiated.
        if (mFragments.size() > position) {
            Fragment f = mFragments.get(position);
            if (f != null) {
                if (mCurTransaction == null) {
                    mCurTransaction = mFragmentManager.beginTransaction();
                }
    
                mCurTransaction.detach(f);
                mCurTransaction.attach(f);
    
                return f;
            }
        }
    ...
    }
    

    This way I am effectively making sure that that the restored Fragments are re-attached to the ViewPager.

    0 讨论(0)
提交回复
热议问题