How to save state of fragment in android? - android

i am using four fragments in one activity
Four buttons at bottom are used to switch between fragments
I have search button of action bar
But when i am clicking on search button keyboard appears and then after that fragment started loading again..
So how to save state of fragment on configuration change..
I have also tried this
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"

Use public void setRetainInstance (true) in fragment.
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setRetainInstance(true);
...
...
}
This will retain only when there is change in orientation, not on back stack.

You can use setRetainInstance(true). No matter where you write - in onActivityCreated or OnCreateView. But in this case you can have problems with memory or link on old Activity in future.
As for me, I think that it's better to save your data in onSaveInstanceState and get them, for example, in onRestoreInstanceState.

Related

Single Activity with Multiple Fragments and Screen Orientation

I'm currently dealing with an issue with Android & It's Re-Creation Cycle on screen rotation:
I have one single Activity and lots of Fragments (Support-V4) within.
For example, the Login it's on a Single Activity with a Fragment, when the logs-in then the App changes it's navigation behavior and uses multiple fragments, I did this, because passing data between Fragment A to Fragment B it's way much easier than passing data Between an Activity A to an Activity B.
So My issue it's presented when I rotate the device, on my first approach, the initial fragment was loaded, but what would happen, if the user it's on Page 15 and it rotates it's device, it would return to Fragment 1 and give a very bad user-experience. I set all my fragments to retain their instance and added this on the MainActivity on Create:
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.main_layout);
initBackStackManager();
initControllers();
mayDownloadData();
setTitle();
if(savedInstanceState == null){
addAreaFragment();
}
}
Now, the first fragment is not loaded after screen orientation change, but If I try to make a fragment transaction, it says Can not perform FragmentTransaction.commit() after onSaveInstanceState(), is there a way to handle this? Or Do I really really need to use multiple Activities with a Fragment embedded within?
Thank you very much!
EDITED
I forgot to add that this happens only on a specific Fragment... For example I have the following fragment flow:
AreaFragment -> WaiterSelectionFragment -> WaiterOptionsFragment.
If I'm in the AreaFragment and I rotate the device I can still add/replace fragments and nothing happens, no error it's being thrown. If I'm on the WaiterSelectionFragment no error happens too. BUT, If I'm on the WaiterOptionsFragment the error it's being thrown. The WaiterSelectionFragment has the following structure:
LinearLayout
FragmentTabHost
Inside the FragmentTabHost there are some fragments, and that's where the error it's happening. You might wonder Why FragmentTabHost? easy, the Customer wants that App to show the TabBar, If I use Native Android Tabs the Tabs get rearranged to the ActionBar when on
Landscape position.
EDIT 2
I've used the method provided by #AJ Macdonald, but no luck so far.
I have my Current Fragment being saved at onSaveInstanceState(Bundle) method and restore my fragment on onRestoreInstanceState(Bundle) method on the Android Activity, I recover my back button and the current Fragment but when I get to the third Fragment the error still occurs. I'm using a ViewPager that holds 4 Fragments, Will this be causing the Issue? Only on this section of the App Happens. I've 4 (main workflow) fragments, on the First, Second and Third Fragment no error it's being presented, only on the ViewPager part.
Give each of your fragments a unique tag.
In your activity's onSaveInstanceState, store the current fragment. (This will probably be easiest to do if you keep a variable that automatically updates every time the fragment changes.)
In your activity's onCreate or onRestoreInstanceState, pull the tag out of the saved bundle and start a new fragment of that type.
public static final int FRAGMENT_A = 0;
public static final int FRAGMENT_B = 1;
private int currentFragment;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//other stuff
if(savedInstanceState == null){
addAreaFragment();
currentFragment = FRAGMENT_A;
}else{
currentFragment = savedInstanceState.getInt("currentFragment");
switch(currentFragment){
case FRAGMENT_A:
addAreaFragment();
break;
case FRAGMENT_B:
addFragmentB();
}
}
}
// when you switch fragment A for fragment B:
currentFragment = FRAGMENT_B;
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
savedInstanceState.putInt("currentFragment", currentFragment);
super.onSaveInstanceState(savedInstanceState);
}
A suggestion to try is to use FragmentTransaction.commitAllowingStateLoss() in place of FragmentTransaction.commit(). That should stop the Exception from being thrown, but the downside is if you rotate the device again the most recent state of the UI may not return. That is a suggestion given that I am not sure of the effect of using FragmentTabHost, if it has any effect at all.

Screen orientation change with setRetainInstance(true)

Hey I would like to ask how to handle screen orientation change in android with the new method setRetainInstance(true) given that it only works with fragments that are not added to backstack. What I currently have is an app which does not use fragments it only uses an activity and uses asynctasks so how would I go about implementing this new change in android in my app
This solution is not viable since I need the layout to change from landscape to portrait android:configChanges="orientation"
You should check out the guides and docs about activitys and fragments
Saving instance state
You have override two methods 1) onSaveInstanceState() and 2) onRestoreInstanceState().
Save all your dynamic data and objects into bundle and retrieve it onRestoreInstanceState().
For example,
#Override
public void onSaveInstanceState(Bundle savedInstanceState){
super.onSaveInstanceState(savedInstanceState);
savedInstanceState.putString("message", text.getText().toString());
}
#Override
public void onRestoreInstanceState(Bundle savedInstanceState){
super.onRestoreInstanceState(savedInstanceState);
message = savedInstanceState.getString("message");
}
"This solution is not viable since I need the layout to change from landscape to portrait"
The point of a retained fragment is that even though the underlying activity may be destroyed on a configuration change, your fragment will not; therefore you can have it persist state through the change.
These links will help you:
Understanding Fragment's setRetainInstance(boolean)
Why use Fragment#setRetainInstance(boolean)?

What can I do to save the instance state of a fragment but still provide a landscape layout?

if the question sounds weird at first, here comes the explanation:
I have got an activity that hosts my three fragments. Since I would like one of my fragments to save its instance state when the device is rotated, I defined this in my manifest for my activity that hosts the fragments:
android:configChanges="orientation|screenSize"
This works just fine. However, now I have got an other problem: One of my other fragments uses a special landscape layout. The problem is, that this layout is not used immediately on device rotation. I think it is because the new layout only gets set on onCreate.
What can I do to solve this problem? I want my landscape layout to be set immediately.
You can put
setRetainInstance(true);
in onCreateView(); method of your Fragment. I think it should do the trick.
As far as I know you down need to add the configChanges parameter to your manifest.
You can override onSaveInstanceState() in your Fragment
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
savedInstanceState.putInt(KEY_INDEX, someIntValue);
}
This methode should be called before your fragment gets destroyed.
Now in your onCreate(Bundle savedInstanceState) (or onCreateView()) methode:
if (savedInstanceState != null) {
someIntValue = savedInstanceState.getInt(KEY_INDEX);
}
This way it shouldn't intervene with any other special fragments.

Android actionbar refresh when rotating and multi-pane

I have an activity the same as the following image:
FragmentA is a listview and has a SearchWidget as menu-item (which is not displayed on old devices, only API11 and above).
FragmentB is a detail view and has several menu-items.
When ActivityA runs on a tablet, the menu-items of FragmentA + FragmentB are visible in the actionbar. This is correct and works perfect.
Now on a Nexus 7 I want a mix of those:
In portrait only use the handset layout
When I rotate the device, the tablet layout is loaded
The only thing which I can't seem to get working is the actionbar. When I rotate the device from landscape mode (tablet view) back to portrait (handset view), still the actionbar shows the menu-items of FragmentA + FragmentB.
I've tried calling the invalidateOptionsMenu() from onResume() in both ActivityA as FragmentA, but without luck.
Does anyone has an idea?
I think this is due activity re-creation process.
When screen is rotated your activity is destroyed (by default).
But before it is destroyed it saves state including states of all currently active fragments.
Later, when activity is creating after orientation change it restores saved state (with both fragments). As details fragment restores it appends menu items.
You can check this by adding log statements or using debugger in onCreateView of DetailsFragment.
If it's your case then you have next solutions:
Suppress saving state (must be avoided if DetailsFragment should keep track of last displayed item or something similar) by removing this fragment before activity save its state.
Suppress any initialization of DetailsFragment if it will not be displayed. Activity should give answer about visibility.
Your variant? I think two approaches above isn't good enough...
Sorry if my answer didn't help you at all
This works for me.
In Activity A try find Fragment B and set setHasOptionsMenu(mTwoPane)
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_document_list);
Fragment fragment = getFragmentManager().findFragmentByTag(TAG_FRAGMENT_DETAIL);
if (getResources().getBoolean(R.bool.has_detail)){
mTwoPane = true;
....
}
if (fragment != null) {
fragment.setHasOptionsMenu(mTwoPane);
invalidateOptionsMenu();
}
}

Fragment Maintain View State on detach/destroy/orientation change

I have an application that has two fragments as actionbar tabs. The fragments are attached/detached when switching between the tabs. Any time I switch a tab, change the orientation, or press back to exit the application, the view is destroyed. I need it to be restored to its previous state when it is reopened. I know, at least on the orientation change, to use onSaveInstanceState and save the data there so I can restore it when the view is recreated. However, for some reason even though the data gets saved properly to the outState bundle and is read properly from the savedInstanceState bundle, the view doesn't update to what it should update to. For example, I start a service and while that service is running I need to hide two buttons and show two other buttons in their place. I use a boolean to check if the service is running, then put it in the outState so I can see which buttons to show or hide. My code for that is:
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean("isRunning", isRunning);
}
In onCreateView:
if (savedInstanceState != null) {
isRunning = savedInstanceState.getBoolean("isRunning", false);
if (isRunning) {
showStopButton();
}
}
And the showStopButton code is:
private void showStopButton() {
btnStart.setVisibility(View.GONE);
btnReset.setVisibility(View.GONE);
btnStop.setVisibility(View.VISIBLE);
btnLoop.setVisibility(View.VISIBLE);
}
So all this works, the boolean is found as true while the service is running, and showStopButton() is called. However, it doesn't appear to actually do anything. The view state just resets itself to as if the first two buttons (which I want to be hidden) are shown instead of the ones I actually want to be shown. Any idea why this is happening/how to fix it?
I also have a listview that I need to stay populated with the same values as before that I can't get to work either.
Also, onSaveInstanceState isn't called when switching tabs (and I think not when pressing the back button either?). How should I go about retaining the view state in these cases?

Categories

Resources