Navigating back to FragmentPagerAdapter -> fragments are empty
Asked Answered
P

5

59

I have a Fragment (I'll call it pagerFragment) that is added to the backstack and is visible. It holds a viewPager with a FragmentPagerAdapter. The FragmentPagerAdapter holds (let's say) two fragments: A and B.

First adding of the fragments works great.

Fragment A has a button that once clicked, adds a fragment (C) to the backstack.

The problem is this: if I add that fragment (C), and then click back, the pagerAdapter is empty, and I cannot see any fragments inside.

If I use a hack, and destroy the children fragments (A and B) in the pagerFragments onDestroyView(), this solves the problem, although I don't wan't to use this hack.

Any ideas what the issue could be?

Parent answered 16/7, 2013 at 9:27 Comment(1)
Possible duplicate of Fragment in ViewPager using FragmentPagerAdapter is blank the second time it is viewedTrifocals
B
93

I had the same problem. The solution for me was simple:

in onCreateView I had:

// Create the adapter that will return a fragment for each of the three
// primary sections of the app.
mSectionsPagerAdapter = new SectionsPagerAdapter(getActivity()
    .getSupportFragmentManager());

where SectionPageAdapter is something like this:

class SectionsPagerAdapter extends FragmentPagerAdapter {
...
}

after changing getSupportFragmentManager to

mSectionsPagerAdapter = new SectionsPagerAdapter(getChildFragmentManager());

it started working!

Bedel answered 18/9, 2013 at 13:21 Comment(6)
getChildFragmentManager() works only 17+ android sdk. :(Phineas
@SachinShelke you need to use Support Library then. ;) developer.android.com/reference/android/support/v4/app/…Kleper
@Mike Mitterer: I tried this and got a "No View Found for FragmentC", because no framelayout with that id exists in the child fragment manager. the fragment manager that recognizes that id is getSupportFragmentManager(), defined in the activity.Jarry
Can anybody explain why getSupportFragmentManager() does not work, but getChildFragmentManager() works? How are views being nested on pressing back? Is there a side effect to this?Hornswoggle
Awesome answer! But, can someone explain why is this needed?Incunabula
Hi mike any solution for below 17 device. because my app min ver-16?Manouch
D
16

It sounds like you are using nested fragments since your ViewPager is inside a PagerFragment. Have you passed getChildFragmentManager() to the constructor of your FragmentPagerAdapter? If not you should.

I don't think you need a FragmentStatePagerAdapter, but I would give that a shot since it handles saving and restoring Fragment state. The fact that your onDestroyView() hack works makes me think that you may want a FragmentStatePagerAdapter.

It could also have something to do with the way the FragmentPagerAdapter adds Fragments. The FragmentPagerAdapter doesn't add Fragments to the backstack. Imagine if you had a 10+ pages added in your ViewPager and the user swiped through them. The user would need to hit back 11 times just to back out of the app.

It may also be related to this post: Nested Fragments and The Back Stack.

Also I'm not sure what you are adding the Fragment C to. Are you adding it to the same container as the ViewPager?

Well at least you have a few options to investigate. In these situations I like to debug down into the Android SDK source code and see what's causing the behaviour. I recommend grabbing the AOSP source and adding frameworks/support and frameworks/base as your SDK sources. That's the only true way to understand what is happening and avoid making random changes until things work.

Dubitable answered 22/8, 2013 at 15:46 Comment(0)
E
4

Use getChildFragmentManager() instead of getSupportFragmentManager(). It will work fine.

Examination answered 15/12, 2017 at 13:26 Comment(0)
M
1

I just faced the problem in our project as well. The root cause is the way the the FragmentPagerAdapter works:

The FragmentPagerAdapter just detaches a Fragment he does not currently need from its View but does not remove it from its FragmentManager. When he wants to display the Fragment again he looks if the FragmentManager still contains the Fragment using a tag that is created from the view id of the ViewPager and the id returned by the adapters getItemId(position) call. If he finds a Fragment he just schedules an attach of the Fragment to its View within the updating transaction of the FragmentManager. Only if he does not find a Fragment this way he creates a new one using the adapters getItem(position) call!

The problem with a Fragment containing a ViewPager with a FragmentPagerAdapter is, that the contents of the FragmentManager is never cleaned up when the containing Fragment is put to the back stack. If the containing Fragment comes back from the back stack it creates a new View but the FragmentManager still contains the fragments that were attached to the old view and the attach of an existing fragment does not work anymore.

The easiest way to get rid of this problem is to avoid nested fragments. :)

The second easiest way is as already mentioned in other posts to use the ChildFragmentManager for the FragmentPagerAdapter as this one gets properly updated during the life cycle of the container fragment.

As there are projects (as my current one) where both options are not possible, I have published here a solution that works with an arbitrary FragmentManager by using the hashCode of the sub fragments as the item id of the fragment at that position. It comes at the price of storing all fragments for all positions within the adapter.

public class MyPagerAdapter extends FragmentPagerAdapter {

private static int COUNT = ...;
private final FragmentManager fragmentManager;
private Fragment[] subFragments = new Fragment[COUNT];
private FragmentTransaction cleanupTransaction;

public MyPagerAdapter(FragmentManager fragmentManager) {
    super(fragmentManager);
    this.fragmentManager = fragmentManager;
}

@Override
public Fragment getItem(int position) {
    return getSubFragmentAtPosition(position);
}

@Override
public int getCount() {
    return COUNT;
}

@Override
public long getItemId(int position) {
    return getSubFragmentAtPosition(position).hashCode();
}

//The next three methods are needed to remove fragments no longer used from the fragment manager
@Override
public void startUpdate(ViewGroup container) {
    super.startUpdate(container);
    cleanupTransaction = fragmentManager.beginTransaction();
}

@Override
public void destroyItem(ViewGroup container, int position, Object object) {
    super.destroyItem(container, position, object);
    cleanupTransaction.remove((Fragment) object);
}

@Override
public void finishUpdate(ViewGroup container) {
    super.finishUpdate(container);
    cleanupTransaction.commit();
}

private Fragment getSubFragmentAtPosition(int position){
    if (subFragments[position] == null){
        subFragments[position] = ...;
    }
    return subFragments[position];
}

}

Military answered 21/11, 2017 at 10:49 Comment(0)
L
0

I had same problem, just set adapter twice at once and that's all.

Example code :

private fun displayImg(photo1:String, photo2:String){
    val pager:ViewPager = v?.findViewById(R.id.ProductImgPager)!!
    val arr = ArrayList<String>()
    arr.add(photo1)
    arr.add(photo2)
    pager.adapter = AdapterImageView(fm, arr ,arr.size)
    pager.adapter = AdapterImageView(fm, arr ,arr.size)

}
Linc answered 19/3, 2020 at 20:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.