How to Maintain Fragment State When Added to the Back Stack

How can I maintain fragment state when added to the back stack?

If you return to a fragment from the back stack it does not re-create the fragment but re-uses the same instance and starts with onCreateView() in the fragment lifecycle, see Fragment lifecycle.

So if you want to store state you should use instance variables and not rely on onSaveInstanceState().

How to maintain fragment state without backstack in tab?

In this case you have to manage fragments' states by yourself. I don't know exactly how your code works so the only thing I can do is to give you some hints.

The first thing you need to implement is saving fragment's state. Let's assume that all fragments have unique ids. In this case you need to create a map that will keep all the states:

private final Map<String, Fragment.SavedState> mFragmentStates = new HashMap<>();

private void saveFragmentState(String id, Fragment fragment) {
Fragment.SavedState fragmentState =
getSupportFragmentManager().saveFragmentInstanceState(fragment);
mFragmentStates.put(id, fragmentState);
}

You need to call this method for a fragment that you're going to remove. Then we need to restore fragment's state and that's how we can do it:

private void restoreFragmentState(String id, Fragment fragment) {
Fragment.SavedState fragmentState = mFragmentStates.remove(id);
if (fragmentState != null) {
fragment.setInitialSavedState(savedState);
}
}

This method you need to call before adding a fragment to a transaction.

The code provided should work fine but to make it work correctly on activity recreation we need to save and restore mFragmentStates properly:

private static final String KEY_FRAGMENT_STATES = "fragment_states";

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
/* Your code ... */

if (savedInstanceState != null) {
Bundle fragmentStates =
savedInstanceState.getParcelable(KEY_FRAGMENT_STATES);
if (fragmentStates != null) {
for (String id : fragmentStates.keySet()) {
Fragment.SavedState fragmentState =
fragmentStates.getParcelable(id);
mFragmentStates.put(id, fragmentState);
}
}
}
}

@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
/* Your code ... */

Bundle fragmentStates = new Bundle(mFragmentStates.size());
for (Map.Entry<String, Fragment.SavedState> entry : mFragmentStates.entrySet()) {
fragmentStates.put(entry.getKey(), entry.getValue());
}
outState.putParcelable(KEY_FRAGMENT_STATES, fragmentStates);
}

Also you can take a look at FragmentStatePagerAdapter class. It uses the same approach for managing states of ViewPager's fragments.

UPDATE: And so your code should end up looking something like this:

private Fragment mCurrentFragment;

public void navigateFragment(String tag, Fragment fragment,
boolean shouldAdd) {
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();

if (shouldAdd) {
mStacks.get(tag).push(fragment); // push fragment on stack
}

if (mCurrentFragment != null) {
saveFragmentState(mCurrentFragment.getClass().getName(), mCurrentFragment);
}

mCurrentFragment = fragment;
restoreFragmentState(fragment.getClass().getName(), fragment);
transaction.replace(android.R.id.tabcontent, fragment);

if (shouldAdd) {
// You shouldn't use back-stack when managing fragment states by yourself.
transaction.addToBackStack(tag);
}

transaction.commit();
}

In this example I use fragment's class name as an id so all the fragment must have different classes. But you can use any other unique value as an id. And another important thing I have to mention is that you shouldn't use back-stack when managing fragment states by yourself. Back-stack performs similar state management and you will likely have conflicts.

How to correctly save instance state of Fragments in back stack?

To correctly save the instance state of Fragment you should do the following:

1. In the fragment, save instance state by overriding onSaveInstanceState() and restore in onActivityCreated():

class MyFragment extends Fragment {

@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
...
if (savedInstanceState != null) {
//Restore the fragment's state here
}
}
...
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);

//Save the fragment's state here
}

}

2. And important point, in the activity, you have to save the fragment's instance in onSaveInstanceState() and restore in onCreate().

class MyActivity extends Activity {

private MyFragment

public void onCreate(Bundle savedInstanceState) {
...
if (savedInstanceState != null) {
//Restore the fragment's instance
mMyFragment = getSupportFragmentManager().getFragment(savedInstanceState, "myFragmentName");
...
}
...
}

@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);

//Save the fragment's instance
getSupportFragmentManager().putFragment(outState, "myFragmentName", mMyFragment);
}

}


Related Topics



Leave a reply



Submit