I am trying to save fragment state in onSaveInstanceState
but when i go back to fragment, it always reloaded again instead of starting from last state.
I looked into onCreateView
and onActivityCreated
and it always have onSaveInstanceState
as null.
public void navigateFragment(String tag, Fragment fragment,
boolean shouldAdd) {
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction ft = manager.beginTransaction();
if (shouldAdd)
mStacks.get(tag).push(fragment); // push fragment on stack
ft.replace(android.R.id.tabcontent, fragment);
if (shouldAdd)
ft.addToBackStack(tag);
ft.commit();
}
As i am unable to use backstack
because in tabs back stack is not useful. Any help would be highly appreciated.
Solution: Save required information as an instance variable in calling activity. Then pass that instance variable into your fragment.
The first key is that we can save our Fragments' state ourselves using FragmentManager. saveInstanceState(Fragment) . Before removing the Fragment, call this method to get a Bundle containing your Fragment's saved state! The second key is restoring state.
Calling addToBackStack() commits the transaction to the back stack. The user can later reverse the transaction and bring back the previous fragment by pressing the Back button. If you added or removed multiple fragments within a single transaction, all of those operations are undone when the back stack is popped.
Use replace() to replace an existing fragment in a container with an instance of a new fragment class that you provide. Calling replace() is equivalent to calling remove() with a fragment in a container and adding a new fragment to that same container. transaction. commit();
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With