I have 3 Fragments and the flow is like
Fragment 1 --> Fragment 2 (adding it to back stack)--> Fragment 3 --> Fragment 1
As I want to go to Fragment 1 from Fragment 3 again, I want the back stack to be clean so that when user press back nothing happens.
Intially I have cleared popback stack at Fragment 3
Boolean isPoped = fManager.popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
And then created a new Instance of my fragment 1
HomeFragment homeFragment = HomeFragment.newInstance(mobileNumber, "");
FragmentTransaction transaction =fManager.beginTransaction();
transaction.replace(R.id.fragment_container, homeFragment);
transaction.commit();
which caused
android: ViewPager throwing null pointer
then I commented the above code and only used
Boolean isPoped = fManager.popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
which works fine as it removed the transactions(that I belive) and took me to homeFragment but that caused View pager not instantiating post fragment popBackStackImmediate
Here I am not getting how popBackStack works? All I want is to take user from Fragment 3 to Fragment 1 and clear the backstack.
I looked at your code in the other question and I think I understand what the problem is.
So your first action is that you create Fragment 1 and put it in a container. I don't see code for that but no matter; if the container is empty then add and replace act the same way. So now your Fragment 1 is in the container.
You haven't called addToBackStack here so the FragmentManager doesn't know how to undo this operation. That's fine, it's the first operation and doesn't need to be undone.
Next you create Fragment 2 and do a replace transaction. Fragment 1 is replaced with Fragment 2. This is pushed to the back stack so the FragmentManager does know how to undo this transaction.
Next you create Fragment 3 and do a replace transaction. Fragment 2 is replaced with Fragment 3. You don't push this operation to the back stack. Uh oh.
Now when you say popBackStack (deferred or immediate) the FragmentManager goes to undo the operation to replace Fragment 2 with Fragment 1 but Fragment 2 isn't in the container anymore. So (from what I understand) the FragmentManager will put Fragment 1 back in the container but leave Fragment 3 there. It doesn't know what to do with Fragment 3.
Here is how I handle those kinds of scenarios: Say I have Fragment 1, then navigate to Fragment 2. To navigate to Fragment 3 when I intend the back button to go to Fragment 1, I do this:
Pop the back stack (immediate, now Fragment 1 is back in the container) then
Replace Fragment 1 with Fragment 3 and push that operation to the back stack.
Now when I press the back button, the FragmentManager knows how to undo my operation and puts Fragment 1 back where Fragment 3 was. This means that I don't have to perform any fragment transactions to handle this back navigation, the FragmentManager handles it all for me.
Related
I am working on an Android app and have encountered an error involving Fragments and FragmentTransactions. I have created an example app to demonstrate the problem. Here is what I am doing:
add Fragment1 to fragmentSpace, without adding to backstack.
replace fragmentSpace with Fragment2, adding to backstack as "beginning".
add Fragment3 to fragmentSpace2 (which is inside of Fragment2), without adding to backstack.
replace fragmentSpace2 with Fragment4, adding to backstack as null.
Call getFragmentManager().popBackStack("beginning", FragmentManager.POP_BACK_STACK_INCLUSIVE); in an attempt to undo all the Transactions, which should bring the user back to seeing only Fragment1.
However, the app crashes on step 5 with this error:
java.lang.IllegalArgumentException: No view found for id 0x7f090004 (me.MitchT.testfragmenttransactions:id/fragmentSpace2) for fragment Fragment3{7f35cb6 #2 id=0x7f090004}
at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:886)
at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:1067)
at android.app.BackStackRecord.popFromBackStack(BackStackRecord.java:1645)
...
I have found that if I add step 3 to the backstack, step 5 no longer crashes the app and there is no more error. Why?
The problem I have with adding step 3 to the backstack is that I want the user to be able to press the back button after Fragment3 is added and go back to Fragment1. If I add it to the backstack, then when the user presses the back button it removes Fragment3, but Fragment2 is still visible, so they must press the back button again. I am trying to avoid that double-back-button behavior.
I did not feel it was necessary to post the code for every single file in the example app on this question, so instead I created a gist.
So I guess my questions are:
Why do I get this error?
Why don't I get the error after adding step 3 to the backstack?
Is there a better way of "going back to the beginning" or going back two fragments at a time?
Thanks!
From what i understand. You have a fragment layout which is the container in activity
Activity hosts Fragment1. Then you replace Fragment 1 by Fragment 2 ( added to back stack.
At this stage clicking back button you will have fragment2 popped and you see the activity which hosts fragment1.
Fragment2 has a framelayout which holds fragment3. Then you have Fragment4 replacing fragment3 which is added to back stack.
Now clicking the button you have getFragmentManager().popBackStack("beginning", FragmentManager.POP_BACK_STACK_INCLUSIVE);. But there is no view with that id.
Just use popBackStack(). Fragment4 --> Fragment2 ( hosting fragment3) --> Fragment1 ( hosted by the activity).
If you want Fragment4 to Activity hosting Fragment1 don't add Fragment4 to the back stack.
Note : In case you want nested fragments you need to use getChildFragmentManager(). https://developer.android.com/about/versions/android-4.2.html
Also note the discussion in the comments sections and the link posted by op Is this the right way to clean-up Fragment back stack when leaving a deeply nested stack?
I reveive an error when trying to pop the backstack after adding several fragments.
In my app I have one main activity, and multiple fragments.
The error occurs in the following way: First the user is in frag 1. He navigates to frag 2, and then to frag 3. This navigation is done by using fragmentTransaction.addToBackStack(null)
After going from frag 1 -> frag 2 -> frag 3, I navigate to frag 4 the same way. However, when I am finished in frag 4, I replace fragment 4 with fragment 3 without adding to the backstack. This way I can include a bundle in the fragment's arguments (instead of just popping the backstack). When I am back in frag 3, and if I hit the back button, the back stack will be popped, but I receive this error:
java.lang.IllegalStateException: Fragment already added: EditFragment...
Note: I do not want to the pop the backstack when going from fragment 4 to fragment 3 because sometimes I end up having a more complicated navigation than the one explained here.
Given the application flow show in the graphic and textually described in the following.
Fragment 1 is the lowest fragment but not in the backstack by setting disallowAddToBackStack.
Fragment 2 is pushed onto the stack, using fragmentTransaction.addToBackStack().
A new instance of fragment 1 is pushed onto the stack.
The top most fragment (fragment 1) is popped from the stack.
Activity 2 becomes foreground.
Activity 1 becomes foreground.
Here is the generalized method I use to handle fragments:
private void changeContainerViewTo(int containerViewId, Fragment fragment,
Activity activity, String backStackTag) {
if (fragmentIsAlreadyPresent(containerViewId, fragment, activity)) { return; }
final FragmentTransaction fragmentTransaction =
activity.getFragmentManager().beginTransaction();
fragmentTransaction.replace(containerViewId, fragment);
fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
if (backStackTag == null) {
fragmentTransaction.disallowAddToBackStack();
} else {
fragmentTransaction.addToBackStack(backStackTag);
}
fragmentTransaction.commit();
}
Problem
When activity 1 resumes in the last step the lowest instance of fragment 1 also resumes. At this point in time fragment 1 returns null on getActivity().
Question
Why is a fragment which is not the top most on the stack resumed?
If resuming the fragment is correct - how should I handle a detached fragment?
When an Activity is not showing UI and then come to show UI, the FragmentManager associated is dying with all of your fragments and you need to restore its state.
As the documentation says:
There are many situations where a fragment may be mostly torn down (such as when placed on the back stack with no UI showing), but its state will not be saved until its owning activity actually needs to save its state.
In your Activity onSaveInstanceState and onRestoreInstanceState, try saving you Fragment references and then restore them with something like this:
public void onSaveInstanceState(Bundle outState){
getFragmentManager().putFragment(outState,"myfragment", myfragment);
}
public void onRetoreInstanceState(Bundle inState){
myFragment = getFragmentManager().getFragment(inState, "myfragment");
}
Try this out and have luck! :-)
I don't see how this would happen, unless (based on how you described the steps) you've misunderstood how fragmentTransaction.addToBackStack() works: it manages which transactions are placed in backstack, not fragments.
From the android docs:
By calling addToBackStack(), the replace transaction is saved to the
back stack so the user can reverse the transaction and bring back the
previous fragment by pressing the Back button.
So if your step 2 looked something like this in code:
fragmentTransaction.replace(containerViewId, fragment2);
fragmentTransaction.addToBackStack();
fragmentTransaction.commit();
and your step 3:
fragmentTransaction.disallowAddToBackStack()//or just no call to addToBackStack - you do not say
fragmentTransaction.replace(containerViewId, newfragment1);
fragmentTransaction.commit();
At this point, Fragment2 will be removed from the backstack, and your backstack consists of the two Fragment1 instances. in Step 4 you pop the top one, which means you should have the bottommost Fragment1 now at the top.
This explains why it is the resumed fragment if you return to the activity. But not, i'm afraid, why it is apparently detached from its activity.
Android OS can and will create and destroy fragments when it sees fit. This is likely happening when you launch Activity 2 and return to Activity 1. I'd verify for sure that it isn't the actively displayed fragment. What is probably happening is that you are seeing it do some of the creation steps for fragment 1 before it does the creation steps for fragment 2.
As for handling the detached fragments you should take a look at this page. The gist of it is that you should only be using the getActivity in certain fragment functions(Based on the fragment life cycle). This might mean that you have to move some of your logic to other functions.
I created a sample app to test this overlapping issue.
I have a fragment type, Fragment1, and I create a new instance of Fragment1 and add it to a FrameLayout in my activity at runtime. I add the fragment with the help of a few buttons.
Note: I have given each new instance of Fragment1 a different number(#1, #2, #3, etc.) to display on the UI to help me figure out which fragment I am viewing.
So.. here is what I do:
Click on Button 3, create new instance of Fragment1 and add it to Frame1.
Click on Button 4, create new instance of Fragment1 and add it to Frame1 and add it to the fragment backstack.
Repeat 1 and 2.
Repeat 1 and 2.
Now, I have fragments in this order: 1(#1),2(#2),1(#3),2(#4),1(#5),2(#6).
I press the back key when viewing fragment #6.
Back key press, UI displays (#5).
Back key press, UI displays (#3 AND #5),
Back key press, UI displays (#1, #3, AND #5)
It seems fragments are getting displayed ON TOP of each other.
WHY? Is there an overlapping issue? How can I clear out this overlapping issue. I thought this would be an issue on the compatibility library... but it is also on 3.0.
Code for adding fragments:
public int doFragmentChange(int cont1, Fragment frag1, String tag1, int cont2, Fragment frag2, String tag2,
boolean addToStack, String stackTag) {
FragmentManager fm = getFragmentManager();// getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
if (frag1 != null) {
ft.replace(cont1, frag1, tag1);
}
if (frag2 != null) {
ft.replace(cont2, frag2, tag2);
}
// add fragment to stack
if (addToStack)
ft.addToBackStack(stackTag);
return ft.commit();
}
If you perform two add calls one after the other (two commit calls) then yes the fragments will appear overlaid, one on top of the other effectively.
So (for new example) if say you replace frag1 with frag2 and then frag3 with frag4 in the same frame with no backstack transaction then I would expect frag2 and frag4 to be overlaid.
Furtheremore there is also a potential issue in your chaining of replace. You should call a separate commit for each. See Android — Replace Fragment Back Stack With New Stack?.
Just override the onBackPress() or onKeyUp and remove the top fragment.
I have 2 main Fragments in my MainActivity
[A] , [B]
and Fragment [A] has 3 more Fragments
[1] , [2] , [3]
And Fragment [1] has also a Fragment and that Fragment also has one more Fragment and this chain goes on.
Now when we press Back button of device, we first check current Fragment in MainActivity and work as :
if (fragment!=null &&fragment.getClass()== Fragment3.class) {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
Fragment tFragment = new Fragment2();
ft.replace(R.id.container, tFragment);
ft.commit();
}
But this isn't a right way to do so I don't get data of Fragment [1] which came from Fragment [A] using Bundle. Please provide a proper way to do this.
I have tried also :
getSupportFragmentManager().popBackStack();
It works for coming into previous Fragment but I cannot called onResume method of that Fragment.
So I wanna know how I can use Back pressed button to handle the data.