How to change fragments within activity and save state - android

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.

Related

Restart activity - dealing with two fragments into a container

I have an activity that has a container. And i have two fragments that I'm replacing into this container. The fragments are fragmentList and fragmentDetail
First time I'm adding the list fragment into the container
MainActivity OnCreate
if (savedInstanceState == null) {
ListFragment listFragment = new ListFragment();
ft.add(R.id.fragment_container, listFragment, "LIST");
ft.commit();
}
When the user clicks on the list, I replace the container with the detail fragment
ActionButton
FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.fragment_container, detailFragment);
fragmentTransaction.commit();
http://www.vogella.com/tutorials/Android/images/xfragmentsusage10.png.pagespeed.ic.MkyF4ZO5Hr.png
The problem came when I restart the activity. How I can know if I need to request the list data again, or if the detail fragment are shown? Because in my onResume method both fragments are null.
you are already checking the savedInstanceState which is your first step
check this document for details:
http://developer.android.com/intl/es/training/basics/activity-lifecycle/recreating.html
the key is very simple, just save your current state in the onSaveInstanceState method, and restore the state on onRestoreInstanceState
the savedInstanceState is a bundle , Bundle objects are used to store and pass data between activities and fragments and so, you need to store a boolean variable in it to determine which fragment are you showing, and restore this fragment on resume according to this boolean flag
You can use
getFragmentManager().FindFragmentByTag(string tag)
And check if it is null and then you can check isVisible on the fragment returned, generally you'll need to reset your adapters as well

Android: Upon Rotation, Current Fragment Disappears (How to Save State)

I've tried searching here and on Google and just cannot figure this out. Any help would be greatly appreciated.
So my application consists of a MainActivity and multiple Fragments, and it utilizes a NavigationDrawer. The drawer has been implemented just as it is in the Android documentation for NavDrawer (using the Support Library v4). Clicking on one of the several ListView Items on the left drawer loads a Fragment like below (this code is in MainActivity):
Fragment fragment = MyAmmunitionFragment.newInstance();
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction()
.replace(R.id.content_frame, fragment, MyAmmunitionFragment.Tag)
.addToBackStack(Tag)
.commit();
mLvLeftDrawer.setItemChecked(position, true);
setTitle(getString(R.string.my_ammunition));
mDrawerLayout.closeDrawer(mLvLeftDrawer);
The problem I'm having is when I rotate the screen. Say one of my Fragments is loaded, like the one above. If the screen is rotated, the current Fragment disappears and my "homescreen" Fragment is displayed. By the way, when the app loads a "homescreen" Fragment is loaded by the Activity, as below:
private void populateMainSummaryFragment(int position, boolean addToBackStack) {
Log.v(Tag, "populateMainSummaryFragment()");
Fragment fragment = MainSummaryFragment.newInstance();
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction()
.replace(R.id.content_frame, fragment);
if (addToBackStack) {
fragmentTransaction.addToBackStack(Tag);
}
fragmentTransaction.commit();
mLvLeftDrawer.setItemChecked(position, true);
//setTitle(getString(R.string.app_name));
mDrawerLayout.closeDrawer(mLvLeftDrawer);
}
I have been able to save any text inputted by the user by overriding onSaveInstanceState() in the Fragment and storing the data in SharedPreferences, then loading from the preferences in the Fragment's onResume() method.
My question is: How do I save the current Fragment itself when the screen rotates (NOT just the data the user may have inputted)?
Currently, I decided to save the current Fragment's tag in SharedPrefs, and then in the MainActivity's "onResume()" method read from SharedPrefs and load the Fragment using the same code as above. It's not working for some reason, and to me, it feels like a hacky/crappy solution. But, if that's what's gotta be done, so be it. It ain't working though, at the moment.
If I can ask a second question, how can I save user inputted data when the screen rotates, and NOT when the user merely navigates to another Fragment? Right now, the user inputted data is saved no matter if the screen is rotated or the Fragment navigated away from. I assume it's just logic and not some specific method call(s), but I'm not seeing the solution.
Thanks a ton for any help!
Save the name of the current Fragment class in the Activity's onSaveInstanceState(). In the Activity's onCreate(),
if(savedInstanceState != null){
/* get the name of the Fragment class that was previously saved
* and load that Fragment, instead of loading HomeFragment. */
}
Now, to check whether the state change is due to rotation, use the following:
Create a class member
private int myOrientation;
In your onCreate(), use
Display display = ((WindowManager)getSystemService(WINDOW_SERVICE)).getDefaultDisplay();
myOrientation = display.getOrientation();
Afterwards, override the method onConfigurationChanged(), and here you can check if the orientation has actually changed or not:
#Override
public void onConfigurationChanged(Configuration newConfig) {
if(newConfig.orientation != myOrientation){
// perform rotation-specific tasks
}
super.onConfigurationChanged(newConfig);
}
Try this. This will work.

Issue with onBackPressed() is not refresh fragment

In my Android Application, I have two Fragments in my Activity, A and B.
In “A” Fragment I have developed one form and in that form some data is coming from a database. I have used a fragment transaction and replaced fragment “A” with fragment “B”.
Here is my code to replace Fragment A with FragmentB.
final FragmentManager fragmentManager = getFragmentManager();
final FragmentTransaction fragmentTransaction=fragmentManager.beginTransaction();
FragmentB reload = new FragmentB();
fragmentTransaction.replace(android.R.id.content,reload);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
In “B” Fragment, I am fetching data from a server and storing it in an SQLite database so we can use the latest data in the application. When I click on the back button it returns back to Fragment “A” but all the data in Fragment “A” is not refreshed based on the newly-reloaded data.
Here is my code for main activity where I override onBackPressed() method:
#Override
public void onBackPressed() {
// TODO Auto-generated method stub
getFragmentManager().popBackStack();
}
I want to refresh Fragment A's data with the latest values from the database when I hit the back button from the main activity. As much I know we cannot do back-press event in fragment B. Please correct me if I am wrong and suggest me how I can solve issue.
Does anybody know how I can achieve it?
If you want to add to back stack then you should use fragmentTransaction.add() . replace will just replace the fragment.
Also the right place to handle pop from backstack is through the use of OnBackStackChangedListener like this
getFragmentManager().addOnBackStackChangedListener(
new FragmentManager.OnBackStackChangedListener() {
#Override
public void onBackStackChanged() {
// find your fragment and do updates
}
});
A good reference Fragments onResume from back stack
When back pressed your Fragment onResume will be called so you can write refresh code in onResume
for better understanding refer below link
http://developer.android.com/guide/components/fragments.html

Empty savedInstanceState Bundle when restoring a Fragment after double Rotation in replacing Fragment

lets call them Fragment A and B. Fragment B is just a detailed view for A which replaces the Fragment A upon a click of a Button in Fragment A.
The replacement code:
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.replace(R.id.fragment_container, new DetailFragment());
transaction.addToBackStack(null);
transaction.commit();
When I now rotate the screen in Fragment B once and press Back the old Fragment A gets restored without any problems (it restores it state in onActivityCreated with the savedInstanceState Bundle).
Now to the fun part...
When I rotate the screen in Fragment B more than once and press Back I get a NullPointerException because int[] data = savedInstanceState.getIntArray(STATE_DATA); in onActivityCreated returns null.
How can I fix this behavior? The only other way I though of was through permanent Storage(Preference or DB) but that seems very inappropriate for the use case.
edit/additional info: The bundle itself is not null, it's just empty
Ok I found the answer:
The following methods from Fragment A get called on rotation change while Fragment B is active:
onSaveInstanceState(), onAttach() and onCreate()
because I am restoring my state in onActivityCreated (which is actually recommended by the sdk!) I lose my variables stored in the bundle after the first rotation because they never get loaded into the local variables that then would be stored on the next onSaveInstanceState. Therefore those values are null when I try to retrieve them after the second rotation.
Solution: Restore the variables in onCreate() so that they are available when onSaveInstanceState is called again.

Fragment overlaps on activity rotation

I'm getting really confused about this. I have an actionbar with list navigation. I click on the list to open 2 fragment one after another and display in the same activity. I'm basically replacing them using this method:
public void openFragment(AprilAppsFragment createdFragment){
if (createdFragment.getClass().isInstance(getDisplayedFragment()))
return;
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
transaction.replace( R.id.main_fragment, createdFragment, "displayed fragment");
transaction.addToBackStack(null);
transaction.commit();
}
I open fragment A, then I open fragment B, then I rotate the screen. Fragment A is being recreated crashing my app
Whys is that, since I'm using replace? How do I avoid recreating fragments that no longer being shown, without losing possibility of back-pressing to them?
Your problem is that you are not saving state for your fragments, and since you haven't added
android:configChanges="orientation|screenSize"
your first fragment is called again, and since you have no saved state your app crashes. So its good that you add the line above in you Activity declaration in the manifest like:
<activity android:name=".yourActivity" android:configChanges="orientation|screenSize"
and Override the onSaveInstanceState and save states for both your fragments. And in your onCreate just do this check:
if(savedInstanceState != null){
fragmentA = (FragmentA) fragmentManager.getFragment(savedInstanceState, stringValueA);
fragmentB = (FragmentB) fragmentManager.getFragment(savedInstanceState, stringValueB);
}
And then check if any of your fragment is not null than create its instance and set that to fragmentTransaction.relplace. Or do this in your for openFragment method.
Cheers,
Fragments save their state (and the location on the back stack) automatically on rotation. Make sure you aren't recreating your fragments or calling openFragment in your onActivityCreated or onCreate methods.
Your problem is that your activity is getting recreated, which is causing you to re-setup your list navigation, which is causing the default (first) position to be triggered. You need to save the selected index between activity destruction and activity recreation. Here's an example:
#Override
public void onCreate(Bundle savedInstanceState) {
// onCreate implementation...
// Must come after setListNavigationCallbacks is called
if (savedInstanceState != null) {
int index = savedInstanceState.getInt("index");
getActionBar().setSelectedNavigationItem(index);
}
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("index", getActionBar().getSelectedNavigationIndex());
}
i had a similar problem and what i do is to add a code line in my
Androidmanifest.xml (inside your activity that u want to stop recreated):
android:configChanges="orientation"
And one more thing, i learned that fragment is a kind of sub activity and doesn't an activity,
We must to extends an activity fragment for getting a support with fragments, and the activity that extands the activity fragment is the actually activity that holds a "sub's activities" that called fragments.
I hope i help u...
Add this to your activity definition in your AndroidManifest.xml file:
android:configChanges="keyboardHidden|screenLayout|orientation|screenSize"
That will keep the activity from restarting and the FragmentManager from automatically recreating the fragments that it has.
If you need the activity to restart on rotate, then you'll have to look for savedInstanceState being passed into onCreate and then query the FragmentManager for your fragments using the tags you supplied.

Categories

Resources