0

Edit: I'm terrible at explaining these things so here is a video http://sendvid.com/mefqxd8f

Context: The application consists of a single Activity with a ViewPager that holds 3 tabs. Tab1 uses an interface called PageFragmentListener to switch between 2 different fragments in the same Tab when a button is pressed. When the app first loads, Fragment1 is inflated in Tab1. After clicking the button, Fragment2 is inflated in Tab1. I'd like Fragment1 to remain in Tab1 permanently after this.

The issue: If Tab3 is currently active (visible to the user) and then the device is rotated Tab1 is completely destroyed. When it is recreated, it inflates Tab1 with Fragment1 instead of restoring Fragment2. In the video you'll see that Tab1 is blue instead of purple.

Question: Is there a way to restore Fragment2 to Tab1 after it is destroyed rather than inflating Fragment1? I'm assuming savedInstanceState can be used somehow.

class MyAdapter extends FragmentStatePagerAdapter {
    static final String TAG = "MyAdapter";
    private final FragmentManager mFragmentManager;
    public BaseFragment mFragmentA;
    public BaseFragment mFragmentB;

    /**
     *  PageFragmentListener for switching fragmentA.
     */
    public PageFragmentListener mListenerA = new PageFragmentListener() {
        @Override
        public void onSwitchToNextFragment(final String id) {
            mFragmentManager.beginTransaction().remove(mFragmentA).commit();
            if (mFragmentA instanceof FragmentA)
            {
                mFragmentA = FragmentA2.newInstance(mListenerA);    // => switch fragment a2
            }
            else if (mFragmentA instanceof FragmentA2){
                mFragmentA = FragmentA.newInstance(mListenerA);
            }
            notifyDataSetChanged();     // notify changes
        }
    };

    /**
     *  PageFragmentListener for switching fragmentB.
     */
    public PageFragmentListener mListenerB = new PageFragmentListener() {
        @Override
        public void onSwitchToNextFragment(final String id) {
            mFragmentManager.beginTransaction().remove(mFragmentB).commit();
            if (mFragmentB instanceof ItemListFragment){     // current fragment is List Fragment
                Bundle arguments = new Bundle();
                arguments.putString(Constants.ARG_ITEM_ID, id);     // selected item id
                mFragmentB = ItemOneDetailFragment.newInstance(mListenerB);       // switch detail fragment
                mFragmentB.setArguments(arguments);

            }else if (mFragmentB instanceof ItemOneDetailFragment) {      // DetailFragment
                mFragmentB = ItemListFragment.newInstance(mListenerB);    // => switch list fragment
            }
            notifyDataSetChanged();     // notify changes
        }
    };

    public MyAdapter(FragmentManager fm) {
        super(fm);
        Log.d(TAG, "MyAdapterConstructor");
        mFragmentManager = fm;
        List<Fragment> fragments = fm.getFragments();
        if(fragments != null){
            for(Fragment f : fragments){
                if (f instanceof FragmentA || f instanceof FragmentA2){
                    mFragmentA = (BaseFragment) f;
                }
                if(f instanceof ItemListFragment || f instanceof ItemOneDetailFragment){
                    mFragmentB = (BaseFragment) f;
                }
            }
        }
    }

    @Override
    public Fragment getItem(int position) {
        Log.d(TAG, "getItem()");

        if (position == 0){      // Tab-1
            if (!(mFragmentA instanceof FragmentA2)) {
                mFragmentA = FragmentA.newInstance(mListenerA);
            }
            return mFragmentA;
        }

        if (position == 1)      // Tab-2
            return FragmentB.newInstance();

        if (position == 2) {    // Tab-3
            if (!(mFragmentB instanceof ItemOneDetailFragment)){
                mFragmentB = ItemListFragment.newInstance(mListenerB);
            }
            return mFragmentB;
        }
        else{
            return null;
        }
    }

    /**
     * To set tab title.
     * @param position the currently visible ViewPager fragment
     * @return the title of the fragment
     */
    @Override
    public CharSequence getPageTitle(int position) {
        Log.d(TAG, "getPageTitle()");
        if (position == 0) {    // Tab-1
            return "Tab 1";
        }
        if (position == 1) {    // Tab-2
            return "Tab 2";
        }
        if (position == 2) {        //Tab-3
            return "Tab 3";
        }
        return null;
    }

    @Override
    public int getCount() {     // Count of Tabs
        return 3;
    }

    @Override
    public int getItemPosition(Object object) {
        Log.i("Adapter", "ItemPosition>>>" + object.toString());
        if (object instanceof ItemListFragment && mFragmentB instanceof ItemOneDetailFragment) {     //  fragment changed
            return POSITION_NONE;
        }

        if (object instanceof ItemOneDetailFragment && mFragmentB instanceof ItemListFragment) {     // fragment changed
            return POSITION_NONE;
        }

        if (object instanceof FragmentA && mFragmentA instanceof FragmentA2) {
            return POSITION_NONE;
        }

        if (object instanceof FragmentA2 && mFragmentA instanceof  FragmentA){
            return POSITION_NONE;
        }

        return POSITION_UNCHANGED;
    }
}
Luke Allison
  • 3,118
  • 3
  • 24
  • 40
  • The explanation of your tabs and fragments quite confusing, will you be more clear? – sandesh May 11 '16 at 07:02
  • Sorry. Please see the video here http://sendvid.com/mefqxd8f . I want the fragment with the purple background to remain after rotating but the fragment with the blue background is loaded instead. – Luke Allison May 11 '16 at 08:14

2 Answers2

0

Simple solution to retain the state of the fragment while rotate is by calling

setRetainInstance(true);

In order to add fragment into stack try like this

transaction.addToBackStack(fragment.getClass.getName());

to get previous fragment (may be on while calling of onBackPressed()) try this

getFragmentManager.popBackStack();
akhil Rao
  • 1,169
  • 7
  • 17
  • Inside fragment class override onCreate() method and in that method call setRetainInstance(true) – akhil Rao May 11 '16 at 07:46
  • The issue persists. Please watch this video of the app. Notice the screen is blue when tab 1 is re-inflated, rather than purple. http://sendvid.com/mefqxd8f – Luke Allison May 11 '16 at 08:02
0

My (poor) solution was to create a Static Class Boolean of MainActivity called hasSavedInstance. I set this to true in onCreate() of MainActivity as follows:

    if (savedInstanceState == null){
        Log.d(TAG, "onCreate() FIRST");
    }
    else{
        Log.d(TAG, "onCreate() ADDITIONAL");
        hasSavedInstance = true;
    }

I then use this value to determine which Fragment to return in MyAdapter:

@Override
    public Fragment getItem(int position) {
    Log.d(TAG, "getItem()");
    if (position == 0){      // Tab-1
        Log.d(TAG, "hasSavedInstance="+MainActivity.hasSavedInstance);
        if (mFragmentA == null && MainActivity.hasSavedInstance){
            mFragmentA = FragmentA2.newInstance(mListenerA);
        }
        else if (!(mFragmentA instanceof FragmentA2)) {
            mFragmentA = FragmentA.newInstance(mListenerA);
        }
        return mFragmentA;
    }

This seems to work but I feel like setting this flag is a very inelegant way of working around this problem.

Luke Allison
  • 3,118
  • 3
  • 24
  • 40