1

I have a simple Pager layout in my application, which should change depending on my AppState. The app has two main states, LoggedIn and LoggedOut. If the AppState is LoggedOut then my tab layout should be built based on two fragments fragment_login and fragment_about. If the App State is LoggedIn then the tab layout should be built based on different tabs fragment_main, fragment_details, fragment_about, fragment_logout.

I can initialise the app with either of these states, and everything works as expected. However, the problem is dynamically changing these layouts at Runtime after the user has logged in.

The Tab layouts change captions correctly, but some of the old tab fragments persist.

SETUP

My Class connecting Fragments and their respective caption

private class FragmentList {
    Class fClass;
    String fName;
    public FragmentList(Class c, String n) {
        fClass = c;
        fName = n;
    }
}

My SectionsAdapter

You can see I'm currently experimenting with passing the ArrayList as a parameter in order to be able to reloadtabs(), but this still isn't working

public class SectionsPagerAdapter extends FragmentPagerAdapter {

    ArrayList<FragmentList> tabList;

    public SectionsPagerAdapter(FragmentManager fm, ArrayList<FragmentList> fragList) {
        super(fm);
        tabList = fragList;
    }

    public void reloadTabs() {
        tabList = listFragments_CurrentlyActive;
        this.notifyDataSetChanged();
    }

    @Override
    public Fragment getItem(int position) {
        Fragment fragment = null;
        Class newFragmentClass;

        if (position <= tabList.size()) {
            newFragmentClass = tabList.get(position).fClass;
        } else {
            newFragmentClass = tabList.get(0).fClass;
        }

        try {
            fragment = (Fragment) newFragmentClass.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        //Bundle args = new Bundle();
        //args.putInt(ARG_SECTION_NUMBER, sectionNumber);
        //fragment.setArguments(args);

        return fragment;
    }

    @Override
    public int getCount() {
        return tabList.size();
    }

    @Override
    public CharSequence getPageTitle(int position) {
        if (position <= tabList.size()) {
            return tabList.get(position).fName;
        } else {
            return "Unknown";
        }
    }
}

Initialising the Fragment Lists

Note: each fragment has its own public static string for FRAGMENTNAME

private ArrayList<FragmentList> listFragmentsLoggedOut = new ArrayList<>();
private ArrayList<FragmentList> listFragmentsLoggedIn = new ArrayList<>();

listFragmentsLoggedOut.add(new FragmentList(fragment_login.class, fragment_login.FRAGMENT_NAME));
listFragmentsLoggedOut.add(new FragmentList(fragment_about.class, fragment_about.FRAGMENT_NAME));

listFragmentsLoggedIn.add(new FragmentList(fragment_main.class, fragment_main.FRAGMENT_NAME));
listFragmentsLoggedIn.add(new FragmentList(fragment_details.class, fragment_details.FRAGMENT_NAME));
listFragmentsLoggedIn.add(new FragmentList(fragment_about.class, fragment_about.FRAGMENT_NAME));
listFragmentsLoggedIn.add(new FragmentList(fragment_logout.class, fragment_logout.FRAGMENT_NAME));

Building the TabLayouts

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    initApp();

    refreshTabsBasedOnNewState();
}

 //Separate method that can be called from elsewhere to update the Tabs
 public void refreshTabsBasedOnNewState() {


    //Can swap these and the app displays and functions correctly.
    listFragments_CurrentlyActive = listFragments_LoggedOut;
    //listFragments_CurrentlyActive = listFragments_LoggedIn;

    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);

    mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager(), listFragments_CurrentlyActive);

    //update adapter class
    mSectionsPagerAdapter.reloadTabs();

    sectionsAdapterACTIVE.notifyDataSetChanged();

    // Set up the ViewPager with the sections adapter.
    mViewPager = (ViewPager) findViewById(R.id.container);
    mViewPager.setAdapter(sectionsAdapterACTIVE);


    TabLayout tabLayout = (TabLayout) findViewById(R.id.tabs);
    tabLayout.setupWithViewPager(mViewPager);

}

THE CURRENT OUTPUT

When the App is hardcoded to be LoggedIn then the two fragments and tabs display correctly. Same with the App being hardcoded to be LoggedOut. However, when I try and change the layout, by changing the value of listFragments_CurrentlyActive then I get the following behaviour:

  • Tabs increase from 2 to 4 and have the correct captions (and ordering)
  • The associated Fragments are 2 of the original ones, and 2 of the new ones.

Effectively, the Tabs are correct, but the fragments being shown are that of: fragment_login, fragment_about, fragment_about, fragment_logout.

So you can see that the 2 additional fragments are being added correctly, but the two initial ones still reference the LoggedOut fragments.

There is probably a very simple solution, where I need to update the adapter somewhere with mSectionsPagerAdapter.notifyDataSetChanged(), or rebuild it, but I'm not finding a fix anywhere. Many thanks. J

EDIT

I've even tried creating the adapters at the start, so they are totally independent, but still the same behaviour occurs

SectionsPagerAdapter sectionsAdapter_LoggedOut = new SectionsPagerAdapter(getSupportFragmentManager(), listFragmentsLoggedOut);
SectionsPagerAdapter sectionsAdapter_LoggedIn = new SectionsPagerAdapter(getSupportFragmentManager(), listFragmentsLoggedIn);

SectionsPagerAdapter sectionsAdapterACTIVE;

sectionsAdapterACTIVE = (AppState.isLoggedIn) ? sectionsAdapter_LoggedIn : sectionsAdapter_LoggedOut

Could it be an issue with the fragment caching in getSupportFragmentManager() ?

EDIT 2

Added this Image to help visualise the issue. Not sure if I was clear enough beforehand.

State Change issues with cached fragments

Jammo
  • 1,838
  • 4
  • 25
  • 39

1 Answers1

1

SOLVED

The problem is described by the following Answer on another thread

If you want to switch out the actual fragments that are being displayed, you need to avoid FragmentPagerAdapter and use FragmentStatePagerAdapter.

FragmentPagerAdapter won't work because it never destroys a fragment after it has been displayed the first time, hence the caching issue I was having. I didn't, however, override getItemPosition(Object item), like that answer suggested. My issue simply went away when I replaced:

public class SectionsPagerAdapter extends FragmentPagerAdapter {

with

public class SectionsPagerAdapter extends FragmentStatePagerAdapter {
Community
  • 1
  • 1
Jammo
  • 1,838
  • 4
  • 25
  • 39