I've got an activity, containing fragment 'list', which upon clicking on one of its items will replace itself to a 'content' fragment. When the user uses the back button, he's brought to the 'list' fragment again.
The problem is that the fragment is in its default state, no matter what I try to persist data.
Facts:
public static TheFragment newInstance(Bundle args)
, setArguments(args)
and Bundle args = getArguments()
FrameLayout
from the parent activity (that is, not nested fragments)setRetainInstance
, because my activity is a master/detail flow, which has a 2 pane layout on larger screens. 7" tablets have 1 pane in portrait and 2 panes in landscape. If I retain the 'list' fragment instance, it will (I think) fuck things up with screen rotationsFragmentTransaction#replace(int, Fragment, String)
, with the same ID but a different tagonSaveInstanceState(Bundle)
, but this is not always called by the framework, as per the doc: "There are many situations where a fragment may be mostly torn down (such as when placed on the back stack with no UI showing), but its state will not be saved until its owning activity actually needs to save its state."
From the bullet 5 above, I guess that low-end devices that need to recover memory after a fragment transaction may call Fragment#onSaveInstanceState(Bundle)
. However, on my testing devices (Galaxy Nexus and Nexus 7), the framework doesn't call that method. So that's not a valid option.
So, how can I retain some fragment data? the bundle passed to Fragment#onCreate
, Fragment#onActivityCreated
, etc. is always null
.
Hence, I can't make a difference from a brand new fragment launch to a back stack restore.
Note: possible related/duplicate question
Since you want only one back stack entry per Fragment , make the back state name the Fragment's class name (via getClass(). getName() ). Then when replacing a Fragment , use the popBackStackImmediate() method. If it returns true, it means there is an instance of the Fragment in the back stack.
Solution: Save required information as an instance variable in calling activity. Then pass that instance variable into your fragment.
Here is an example: @Override protected void onCreate(Bundle savedInstanceState) { super. onCreate(savedInstanceState); if (savedInstanceState == null) { myFragment = MyFragment. newInstance(); getSupportFragmentManager() .
A fragment is a reusable class implementing a portion of an activity. A Fragment typically defines a part of a user interface.
This doesn't seem right, but here's how I ended up doing:
public class MyActivity extends FragmentActivity {
private Bundle mMainFragmentArgs;
public void saveMainFragmentState(Bundle args) {
mMainFragmentArgs = args;
}
public Bundle getSavedMainFragmentState() {
return mMainFragmentArgs;
}
// ...
}
And in the main fragment:
public class MainFragment extends Fragment {
@Override
public void onActivityCreated(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle args = ((MyActivity) getActivity()).getSavedMainFragmentState();
if (args != null) {
// Restore from backstack
} else if (savedInstanceState != null) {
// Restore from saved instance state
} else {
// Create from fragment arguments
args = getArguments();
}
// ...
}
// ...
@Override
public void onDestroyView() {
super.onDestroyView();
Bundle args = new Bundle();
saveInstance(args);
((MyActivity) getActivity()).saveMainFragmentState(args);
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
saveInstance(outState);
}
private void saveInstance(Bundle data) {
// put data into bundle
}
}
It works!
onDestroyView
onSaveInstanceState
setArguments
All events are covered, and the freshest information is always kept.
It's actually more complicated, it's interface
-based, the listener is un/registered from onAttach
/onDetach
. But the principles are the same.
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