I implement the app which has two layout for landscape and portrait mode. The layout for landscape is in the layout-land. I have the fragment1 for portrait layout and fragment2 for landscape layouts. I override the onCreateView in each fragment.
I have static variable to know the current fragment. I assgined in the onCreateView (1 for fragment1 and 2 for fragment2).
My problem is that the static value is still 1 when the orientation is landscape mode.
I debugged the orientation of application. When I change orientation portrait into landscape, fragment2's onCreateView method called first and then the fragment1's onCreateView method called again. The static value has overridden.
I don't know why did fragment1 onCreateView method call after the fragment2 called? I want to assign the right value for right fragment.
Sorry for my bad English.
Thanks.
You don`t need to save something!
Just let your activity handle the orientation change.
In AndroidManifest.xml put this
<activity
android:name=".MainActivity"
android:configChanges="orientation|screenSize" >
</activity>
You need to save the bundle and must override onsavedinstance method so that the activityis not createdagain.
First when orientation is changed android checks for the savedinstancestate and calls onSavedInstanceState method if implemented.
Inside the onCreate of each fragment you have to call
getActivity().setRequestedOrientation(requestedOrientation)
You should have two special Fragment implementations, if your Fragments have a different 'business logic' in landscape and portrait. If they just have a different layout then use 1 Fragment implementation, and create 2 layouts, one for each orientation.
Instantiate and create your Fragments in Activity.onCreate(). But do not save the current Fragment in a static variable. Instead ask the FragmentManager if a Fragment has already been added:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Fragment myFragment = getSupportFragmentManager().findFragmentByTag("myTag");
if(myFragment == null){
//no Fragment has been added
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.add(new MyFragment1(), "myTag");
transaction.commit();
}else{
//the fragment has been added already, possibley before orientation change
//you could check its type and replace it
if(fragment instanceof MyFragment1){
}else if(fragment instanceof MyFragment2{
}
}
}
Related
I have a thread which triggers a callback method in my Activity which has a fragment. The Activity then calls a method within the Fragment to refresh the UI with new info. In that method, getString() is called. When getstring() is called, I get a 'Fragment not Attached to Activity error'. This UI refreshing error is guarded runOnUIThread. The fragment is added like so:
FragmentManager fm = getSupportFragmentManager();
fm.beginTransaction()
.add(fragmentContainerID, fragment, tag)
.commit();
fm.executePendingTransactions();
The activity uses two different layout files for horizontal and vertical orientation. Their are two fragment containers in both layouts. They have the same names in both horizontal and vertical layouts, but they do not match each other. The fragments are added using the above code the first time the Activity is created. On orientation changes, the fragments are automatically added into the layouts because the fragment containers have the same ids in both vertical and horizontal orientation.
When the savedInstanceState is not null, I get a reference to the fragments by using
fm.findFragmentByTag
Can anyone please tell me what I am doing wrong ? The fragments are visibly added to the Activity perfectly fine, and several minutes go by before this thread callback occurs, so I don't see how the Fragment is not "attached".
EDIT: Sigh, after some debugging Ive found that adding fragments in this way is perfectly fine. Code elsewhere in the Activity ( I registered for a callback twice) caused my fragment not attached error. If anyone reading this is getting this error, keep in mind that this issue can be triggered from elsewhere. Also, you have to setRetainInstance to true in the fragment to avoid it being recreated. Tags alone don't do that.
Fragments are added one-per-container so if you are using .add(fragmentContainerID, fragment, tag) ensure firstly that fragmentContainerID is different for each of the fragments added.
However, these sorts of issue are most often caused by a misunderstanding in how orientation changes are handled. When you change orientation and let the oncreate handle the adding: you get a new fragment of the same type what this means is that if your background task has a reference to your fragment - it has a reference to the old (now detached) fragment. What you need to do is to make sure the same fragment gets added to the tag (not a new instance). To do that just don't (re)create a fragment instance when we are only restoring state
public class MyActivity ... {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FragmentManager fm = getSupportFragmentManager();
if (savedInstanceState == null) {
MyFragment frag = new MyFragment();
frag.setArguments( getIntent().getExtras() ); //if it takes params
fm.beginTransaction().add(fragmentContainerID, frag, tag).commit();
}
//after this point a call to MyFragment frag = (MyFragment) fm.findFragmentByTag(tag); should always return the correct fragment
}
It's probably also a wise idea for your task to look for the fragment right when it does its callback to update the UI (and NOT to hold on to a reference to it). Depending on how you've set up your task it just means changing from FragmentManager fm = getSupportFragmentManager(); then the task is created, to calling it as part of the onPostExecute
When change orientation, fragment automatically added to FragmentManager and forget own tag!!
Use this code to find your fragment:
FragmentManager fragmentManager= getSupportFragmentManager();
for (Fragment fragment : fragmentManager.getFragments())
{
if(fragment instanceof MyFragment)
myFragment = (MyFragment)fragment;
}
After you find the fragments on orientation change, try detaching them and then reattaching them before any operation. Or if that does not work remove them and add them. then continue..... I have even had to sometimes put a time delay of a few millisec after detach and before attach to ensure all OK.
I have a fragmentactivity that starts on Fragment A then can be change to Fragment B. If i am on Fragment B and i rotate my device. It loads the original Fragment A not Fragment B. I am Loading both Fragment A and B pragmatically. I thought android was supposed to save which Fragment i was on automatically i am not overriding onSaveInstanceState
This is how i am loading the fragments
FragmentTransaction t = this.getSupportFragmentManager()
.beginTransaction();
t.replace(R.id.fragholder, new MainFragment());
t.commit();
When you rotate your device, it destroys your Activity and recreates its. So assuming Fragment A is the default Fragment, then it is logical that it would load that when the Activity is recreated. So you would need to override onSaveInstanceState to store which Fragment is visible and then reload that Fragment in onCreate.
Reference to relevant Activity Lifecycle doc:http://developer.android.com/training/basics/activity-lifecycle/recreating.html
Just add android:configChanges="orientation|screenSize" to your manifest and android takes care of everything for you
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.
I have an activity which loads a fragment (say Fragment1) in onCreate. When the user presses a button in Fragment1, I replace Fragment1 with a new fragment, say Fragment2. The problem is that on changing orientation while in Fragment2, the activity is recreated and shows Fragment1
instead of Fragment2. (because I create Fragment1 in onCreate)
How can I stick to Fragment2 and also retain its state on orientation change?
Thanks
When a fragment transaction occurs, assign a variable so you know which fragment is being created. Then override OnSavedInstanceState and pass your variable into the bundle.
Then on orientation change, onCreate will be called and you can retrieve your variable from the savedInstanceStateBundle. You can then choose which fragment will be 'loaded' in the onCreate.
I have a ListFragment where I use a custom adapter to populate the listview. All is well until I change orientation and scroll. Then it looks like this:
I am guessing it has something to do with me fumbling with a view holder, but I can't access the code right now.
The reason for the overlapping fragment was that I used FrameLayout and added the fragment with FragmentTransition.add(...). When I changed .add() to .replace() the old fragment was removed and the new one was added and my problem was solved.
I had similar problem and according to that i am telling the solution :-
you are getting this blur because every time on orientation change somewhere new instance of listfragment is created (may be it is in oncreate()), so you have to just make an instance of list fragment once and on orientation change replace that fragment rather than adding again.
Changing orientation causes onCreate to restart unless you include this method
public void onConfigurationChanged(Configuration newConfig)
{
super.onConfigurationChanged(newConfig);
}
and put this in the activity section of your manifest
android:configChanges="orientation"
Check that this is the first time onCreate() is called,
in other words, determine if the callback is not due to screen rotation.
protected void onCreate(Bundle savedInstanceState) {
if(savedInstanceState == null) {
// transition.add(Your fragment,...)
}
}
I had the same problem, and its because of FrameLayout usage,
First you have to check if your fragment has already added to the activity :
String TagName = "F";
Adding a fragment without a UI If you want to get the fragment from the activity later, you need to use findFragmentByTag(). "Google"
http://developer.android.com/guide/components/fragments.html#Adding
Fragement F = getFragmentManager().findFragmentByTag(TagName);
Then check
if(F == null) {
F = new F();
getFragmentManager()
.beginTransaction()
.add(R.id.container, F, TagName)
.commit();
}
the target here is to avoid adding or creating new instance of the fragment, that persist during the configuration change which causes the problem when using the FrameLayout as container.
Solution 2 (Simple):
The only thing you have to do here is to change your container to ex:LinearLayout , and that's it. but in my opinion this is not the best solution because of multiple instances of the fragment.
Hope this help;