Restoring state of fragment - android

I have a Fragment F attached on Activity A. When another activity becomes front-activity it is called onSaveInstanceState. I've overrided that function and it looks like:
public void onSaveInstanceState(Bundle outState){
saveState_to_outstate
}
Now, when the Fragment F is front again it is called onViewCreated(View view, Bundle savedInstanceState). I would like to restore previously saved state but I cannot because savedInstanceState is null though it was written before in onSaveInstanceState.
Why it happened?

If you want to restore Fragment F with previous Data. As according to my knowledge you should perform inside onViewStateRestored() method of Fragment.
OnViewStateRestored (Bundle) tells the fragment that all of the saved state of its view hierarchy has been restored.
you Should visit this link for your reference:Fragment

Method A: You can pass the data from Activity A to Fragment F by bundle or intent. it's similar to usage of saving instance.
Send data from activity to fragment in android
MethodB: You can only use following function with onCreate() at the same place, either fragment F or Activity A.
public void onSaveInstanceState(Bundle outState){
outState.putInt("STATE_NAME", 123);//123 is the data you gonna save
super.onSaveInstanceState(outState);//Don't forget to put this
}
MethodC: Use hardcode-like SharePreference Android Shared preferences example ,it's also like the bundle and intent, and it won't be effected by Activity or fragment life periods.

Related

How to change fragments within activity and save state

I have an activity that would have fragments dynamically added using the method below.
FragmentTransaction transaction=getSupportFragmentManager().beginTransaction();
FragmentA fragmentA = new FragmentA();
transaction.add(R.id.fragment_container, fragmentA, "fragmentA");
transaction.addToBackStack("fragmentA");
transaction.commit();
FragmentA has a TextView. I have a navigation drawer on my activity and want to switch between fragments (for example FragmentA, FragmentB, and FragmentC) depending on which item was clicked in the navigation drawer. How do I save the state of the fragments when changing to another fragment. I've implemented onSavedInstance(Bundle outState) and onActivityCreated(Bundle savedInstanceState) but savedInstanceState is always null. I want to be able to save the fields of FragmentA when changing from FragmentB and then changing back to FragmentA from FragmentB.
I cannot save the state when pressing the backstack. It seems like the fields are not being saved.
What are the correct ways to do this?
Fragment's onSaveInstanceState(Bundle outState) will never be called unless fragment's activity call it on itself and attached fragments. Thus this method won't be called until something (typically rotation) force activity to SaveInstanceState and restore it later.
So In on createView you can do something like that
.
.
.
Bundle mySavedInstanceState = getArguments();
String value = mySavedInstanceState.getString(KEY);
.
.
Save value in onPause() method
#Override
public void onPause() {
super.onPause();
String value = //get value from view;
getArguments().putString(KEY, value);
}
You can use SharePrefrences to save Data in FragmentA and read it when it is called again. Additional advantage is that the Data saved in SharePreferences can be read in other fragments if need be.
Useful Links on Fragments;
- https://developer.android.com/training/basics/fragments/index.html
-http://www.vogella.com/tutorials/AndroidFragments/article.html
I found a quick way to maintain the state of each fragment at the below link.
How do I restore a previously displayed Fragment?
I didn't realize that FragmentTransaction had a hide() method to hide the view of the current fragment and a show() method to show it again.

Cannot save and restore a nested Fragment?

I have a nested Fragment that I am trying to restore the state given an orientation change.
So firstly my setup is as follows:
Activity -> ParentFragment (SetRetainInstance(true)) -> ChildFragment
In My Child fragment I have the onSaveInstance code as follows:
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// Serialize the current dropdown position.
outState.putInt(STATE_SELECTED_NAVIGATION_ITEM, getActivity().getActionBar()
.getSelectedNavigationIndex());
}
However when I orientate the device in all the LifeCycle events return a savedInstance state of null.
Am I doing this incorrectly for a ChildFragment? Why is my state not getting saved and returned?
It's due to setRetainInstance(true) of your parent fragment. Android retains a fragment with all its children fragments. So your ChildFragment is not destroyed, and that's why you get null in savedInstanceState. The documentation of onCreateView states:
savedInstanceState If non-null, this fragment is being re-constructed from a previous saved state as given here.
You can try to comment setRetainInstance(true) out and ensure you get correct value for savedInstanceState.

How does `onViewStateRestored` from Fragments work?

I am really confused with the internal state of a Fragment.
I have an Activity holding only one Fragment at once and replaces it, if another Fragment should get shown. From the docs onSaveInstanceState is called ONLY if the Activitys onSaveInstanceState is getting called (which isn't called in my case).
If I stop my Fragment, I'll store its state myself inside a Singleton (yeah, I know I hate Singletons, too, but wasn't my idea to do so).
So I have to recreate the whole ViewHirarchy, create new Views (by using the keyword new), restore its state and return them in onCreateView.
I also have a Checkbox inside this View from which I explicitly do NOT want to store its state.
However the FragmentManager wants to be "intelligent" and calls onViewStateRestored with a Bundle I never created myself, and "restores" the state of the old CheckBox and applies it to my NEW CheckBox. This throws up so many questions:
Can I control the bundle from onViewStateRestored?
How does the FragmentManager take the state of a (probably garbage-collected) CheckBox and applies it to the new one?
Why does it only save the state of the Checkbox (Not of TextViews??)
So to sum it up: How does onViewStateRestored work?
Note I'm using Fragmentv4, so no API > 17 required for onViewStateRestored
Well, sometimes fragments can get a little confusing, but after a while you will get used to them, and learn that they are your friends after all.
If on the onCreate() method of your fragment, you do: setRetainInstance(true); The visible state of your views will be kept, otherwise it won't.
Suppose a fragment called "f" of class F, its lifecycle would go like this:
- When instantiating/attaching/showing it, those are the f's methods that are called, in this order:
F.newInstance();
F();
F.onCreate();
F.onCreateView();
F.onViewStateRestored;
F.onResume();
At this point, your fragment will be visible on the screen.
Assume, that the device is rotated, therefore, the fragment information must be preserved, this is the flow of events triggered by the rotation:
F.onSaveInstanceState(); //save your info, before the fragment is destroyed, HERE YOU CAN CONTROL THE SAVED BUNDLE, CHECK EXAMPLE BELLOW.
F.onDestroyView(); //destroy any extra allocations your have made
//here starts f's restore process
F.onCreateView(); //f's view will be recreated
F.onViewStateRestored(); //load your info and restore the state of f's view
F.onResume(); //this method is called when your fragment is restoring its focus, sometimes you will need to insert some code here.
//store the information using the correct types, according to your variables.
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putSerializable("foo", this.foo);
outState.putBoolean("bar", true);
}
#Override
public void onViewStateRestored(Bundle inState) {
super.onViewStateRestored(inState);
if(inState!=null) {
if (inState.getBoolean("bar", false)) {
this.foo = (ArrayList<HashMap<String, Double>>) inState.getSerializable("foo");
}
}
}

FragmentActivity onSaveInstanceState not getting called

I have seen a few similar questions about onSaveInstanceState not getting called for Fragments, but in my case Fragments work fine, it's the main FragmentActivity that's having trouble.
The relevant code looks fairly simple:
public class MyFActivity extends FragmentActivity implements ActionBar.TabListener {
String[] allValues; // data to save
#Override
protected void onSaveInstanceState (Bundle outState) {
Log.d("putting it!", allValues.toString());
outState.putStringArray("allValues", allValues);
super.onSaveInstanceState(outState);
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
allValues = savedInstanceState.getStringArray("allValues");
Log.d("getting it!", allValues.toString());
}
}
}
When pausing the activity (using the back button), the onSaveInstanceState is never called, and consequently, savedInstanceState is always null within the onCreate method upon resuming the app. I tried adding a block like this:
#Override
public void onPause() {
super.onPause();
onSaveInstanceState(new Bundle());
}
which was suggested in https://stackoverflow.com/a/14195202/362657 but while onSaveInstanceState then gets called, savedInstanceState remains null within onCreate method. What am I missing?
The issue here is that you are misunderstanding how onSaveInstanceState works. It is designed to save the state of the Activity/Fragment in the case that the OS needs to destroy it for memory reasons or configuration changes. This state is then passed back in onCreate when the Activity/Fragment is returned to / restarted.
In a Fragment, all of their lifecycle callbacks are directly tied to their parent Activity. So onSaveInstanceState gets called on the Fragment when its parent Activity has onSaveInstanceState called.
When pausing the activity (using the back button), the onSaveInstanceState is never called, and consequently, savedInstanceState is always null within the onCreate method upon resuming the app.
When pressing back, the user is destroying the Activity, and therefore its children Fragments, so there is no reason to call onSaveInstanceState, since the instance is being destroyed. When you reopen the Activity, it's a brand new instance, with no saved state, so the Bundle passed in onCreate is null. This is behaving exactly as designed. However, try rotating the device or hitting the home button, then you will see the Activity and its children Fragments have onSaveInstanceState called, and passed back in onCreate when returned to.
The hack you added, directly calling onSaveInstanceState(new Bundle()); inside of onPause, is a very bad practice, as you should never call the lifecycle callbacks directly. Doing so can put your app into illegal states.
If what you really want is for your data to persist beyond an instance of your app, I suggest you look into using SharedPreferences or databases for more advanced data. You can then save your persistent data in onPause() or whenever it changes.
In an update to the accepted answer:
A fragment's onSaveInstanceState may be called if you are using a ViewPager with a FragmentStatePagerAdapter (rather than FragmentPagerAdapter)
FragmentStatePagerAdapter
This version of the pager is more useful when there are a large number of pages, working more like a list view. When pages are not visible to the user, their entire fragment may be destroyed, only keeping the saved state of that fragment. This allows the pager to hold on to much less memory associated with each visited page as compared to FragmentPagerAdapter at the cost of potentially more overhead when switching between pages.
And don't forget:
When using FragmentPagerAdapter the host ViewPager must have a valid ID set.
Not an accurate answer to the question, but may help someone's day.
In my case, I called
#Override
public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {
super.onSaveInstanceState(outState, outPersistentState);
}
I replaced the above code as below and things worked
#Override
protected void onSaveInstanceState(#NonNull Bundle outState) {
super.onSaveInstanceState(outState);
}

Android muliple WebView save instance

I try to save and restore the state of 3 WebViews in my activity by using onSaveInstanceState() and the restoreState() method of my WebView in onCreate(). This works just fine when I had only one WebView, but with 3 WebViews to save, it looks like it's only the last one save which counts and overrides the other saves; and so when I use the restoreState() method in onCreate(), the first WebView has the content of the third WebView and the 2 others are empty.
Here is a sample of my activity code:
#Override
public void onSaveInstanceState(Bundle outState) {
((WebView)findViewById(R.id.webview1)).saveState(outState);
((WebView)findViewById(R.id.webview2)).saveState(outState);
((WebView)findViewById(R.id.webview3)).saveState(outState);
}
#Override
public void onCreate(Bundle savedInstanceState) {
((WebView)findViewById(R.id.webview1)).restoreState(savedInstanceState);
((WebView)findViewById(R.id.webview2)).restoreState(savedInstanceState);
((WebView)findViewById(R.id.webview3)).restoreState(savedInstanceState);
}
Should I use some trick to use another bundle for each view? Have I done something wrong somewhere else in my code that causes this or is it the standard behaviour?
Your 2nd call of saveState() overwrites the contents saved in the first call and the 3rd call overwrites the content of the 2nd call. At the end of the execution of onSaveInstanceState() the bundle only contains the 3rd WebView's state.
This is why when you restore the state of the 3rd WebView gets restored to the 1st WebView.
Create a new bundle object of each of your WebViews and put them into the outState Bundle object using this giving a different key for each Bundle:
public void putBundle (String key, Bundle value)
In onCreate() get the Bundles based on the 3 keys and restore the states.

Categories

Resources