When using FragmentActivity it automatically restores fragment state and recreates all fragments.
I know this is done mainly saving the state in onSaveInstanceState and then restored in activity's onCreate. Looking a little on the code I've seen that all fragments are recreated (or only attached if retainInstance is true) and added to the FragmentManager but it's not clear to me in which way they are added to the view, because view isn't automatically restored.
My original problems were that I get duplicates of some fragments similar to that other question.
I workarrounded that in onCreate with:
Fragment f = fm.findFragmentByTag(tagName);
if(f==null) {
f = createFragment();
fm.beginTransaction().add(R.id.myContainer,f,tagName).commit();
} else {
//Nothing it's on the view
}
Now it works, but I still doesn't understand completely how it works.
My doubts are:
In which moment and how are fragments attached to the View? I've experimented that fragment restoration is done in onCreate of FragmentActivity. But if I call setContentView after that, how the fragment attach to the view?
Can I prevent fragment recreation without overwriting onSaveInstanceState? Because due to different orientation layouts with different number of fragments my original intention was to recreate only one state fragment marked as retained an don't restore the other view fragments that are not marked as retained.
View hierarchy in not restored automatically. So, in Fragment.onCreateView() or Activity.onCreate(), you have to restore all views (from xml or programmatically). Each ViewGroup that contains a fragment, must have the same ID as when you created it the first time. Once the view hierarchy is created, Android restores all fragments and put theirs views in the right ViewGroup thanks to the ID. Let say that Android remembers the ID of the ViewGroup on which a fragment was.
This happens somewhere between onCreateView() and onStart().
I think it could be possible to keep fragment recreation but, on the ViewGroup that hold the fragment, set visibility to GONE. In this way, the fragment doesn't appear and you can remove it programmatically later.
Related
Background:
I have a main Activity, it wraps a main Fragment that can be changed, and in order to keep a backstack I use FragmentManager's backstack.
The main difference from keeping an activity stack is that when a fragment is pushed to the backstack and get replaced it will call it's onDestroyView() but not it's onDestroy(), and when it get back it's view will be re-created with onCreateView(). (however onCreate() is not called as the fragment object is not disposed)
In an activity stack it won't happen and the views remain.
This has a positive effect on low-end devices as the Android OS can free some memory and you don't have to keep the views right (in my app messages from the server might change the view in any time) so one can save precious bandwidth as well.
The Actual Problem:
Let's say I have a fragment and the user click on something and it's view is changed, e.g. a list is expanded.
If the user then go to another screen (i.e. fragment) the previous fragment will be pushed to the backstack and it's view will be destroyed.
When the user is going back, the fragment will be re-created and will not "remember" the changes the user had made, e.g. the list would not be expanded as it should
so how can I save the state and restore it without making special cases for every view?
Undesired Answers:
keep the view alive: doing something to keep the view would break the fragment efficiency
using onSaveInstanceState(): it will not get called when the fragment is pushed to the backstack as the activity is not destroyed and that's not a configuration change.
special object: prefer not to do it if there is a way the system can do it for you.
I currently am using ActionBar's tab feature with multiple fragments each representing a tab.
On my last fragment there is a 'Submit' button that takes all the input that the user has done on all the fragments and submits it.
My problem is that since those other fragments have been detached (only the currently showing fragment is attached to the main view) their view gets destroyed so cannot be accessed using the fragment's findViewById().
How can I access the data the user inputted for these fragments? Interestingly, it does store all the inputted values in a bundle and restores them when the fragment is attached again, I'm thinking the answer lies somewhere there...
I fixed this by not calling .detach() but .hide().
Now in the activity findViewById() will actually be able to access the view of all its fragments since their UI is now not detached just not showing.
I add my views programmatically.How i can restore states of my views after the fragment is killed ?
I think it can be done with `onViewStateRestored, but i can not configure it's properly.
How i can do it in a right way?
No matter how do you creating views (programmatically or in layout file). Save current fragment state in onSaveInstanceState() and restore it in onCreateView().
See the onSaveInstanceState() reference for further details.
I have a tab host control that is loading tabs using fragments.
Each time a tab is switched it detaches the old fragment and attaches the new fragment.
I noticed that the OnCreateView method is called during this process, and that a lot of my state is getting lost since it recreates the view each time. However I noticed that some view state such as the value of edit text is being maintained across detach/attach.
I'm wondering how Android is automagically restoring state when the view is being completely destroyed and recreated as a new view. The value of the Bundle savedInstanceState is always null when I am just switching tabs. Bundle savedInstanceState only becomes populated when I do something like rotating the screen.
as far as I can tell this restoring of state is taking place just before the fragment onStart method is called.
When attaching and detaching fragments only the views are destroyed, the fragment instance remains the same.
The fragment manager restores the states of views that have ids, and the savedInstanceState is null.
In case of rotation, the fragments are probably recreated by you somewhere else (in activity's onCreate()?).
When a fragment is about to be removed from the window (or replaced) its onSaveInstanceState(Bundle) (or onRestoreInstanceState(Bundle)) method is called. This will propagate through the fragments hierarchy restoring its previous state.
I have a custom widget that performs FragmentTransaction.replace when buttons are pressed. Currently, my code is set up such that the first time a fragment is created, it attaches a bunch of stuff to the view that isn't originally part of the xml layout file.
When the app first launches, all my fragments show stuff correctly, however, let's say I start on Fragment A. I can then transition to Fragment B (with B showing up correctly), however, when I transition back to Fragment A, all the stuff I have attached to the view of Fragment A is now gone. I know this happens because onCreateView is called which probably means the Fragment's view is re-generated when FragmentTransaction.replace is called.
Is there a way where I can keep my fragments around instead of having them re-generate their views when FragmentTransaction.replace is called?
Thanks!
Instead of using fragmentTransaction.replace, use fragmentTransaction.show and fragmentTransaction.hide.
That will keep your fragments from being destroyed.