I have Fragment1. On them, i pressed button and start DialogFramgent. On DialogFragment i pressed button and start Fragment2. When i pressed back, i am return to Fragment1, but not to DialogFragmnet
You can add the fragment to the backstack. A call addToBackStack() should solve the problem. Straight from the documentation: "This back stack is managed by the activity and allows the user to return to the previous fragment state, by pressing the Back button". See for details:
http://developer.android.com/guide/components/fragments.html
Jasper
A great feature about using fragments in your activity is the ability to add, remove, replace, and perform other actions with them, in response to user interaction. Each set of changes that you commit to the activity is called a transaction and you can perform one using APIs in FragmentTransaction. You can also save each transaction to a back stack managed by the activity, allowing the user to navigate backward through the fragment changes (similar to navigating backward through activities).
You can acquire an instance of FragmentTransaction from the FragmentManager like this:
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
Each transaction is a set of changes that you want to perform at the same time. You can set up all the changes you want to perform for a given transaction using methods such as add(), remove(), and replace(). Then, to apply the transaction to the activity, you must call commit().
Before you call commit(), however, you might want to call addToBackStack(), in order to add the transaction to a back stack of fragment transactions. This back stack is managed by the activity and allows the user to return to the previous fragment state, by pressing the Back button.
Related
I'm having problem with fragment. Lets try to understand my issue, I have two fragment A and B. When app start with main activity,i start fragment A as you can see :
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container,new MusicFragment())
.commit();
When i click on a button, it starts fragment B
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container,new BarFragment())
.addToBackStack(null)
.commit();
Main problem is after starting fragment B,when i pressed back to go back to fragment A , Fragment A Recreated with new state.
I don' want to recreate fragment A. I only want to start fragment from old state where i left. How to fix it ?
Instead of calling the replace method you should be calling the add method with a subsequent call to addToBackStack with an argument of null. The add method adds the fragement to the stack with a null tag and calling the addToBackStack with an argument of null then the current fragment is stopped upon commit. If the method is not called then the current fragment is destroyed and recreated when coming back.
getSupportFragmentManager().beginTransaction()
.add(R.id.fragment_container,new BarFragment())
.addToBackStack(null)
.commit();
You can clearly find it in the documentation quote saying this:
If you don't call addToBackStack() when you perform a transaction that
removes a fragment, then that fragment is destroyed when the
transaction is committed and the user cannot navigate back to it.
Whereas, if you do call addToBackStack() when removing a fragment,
then the fragment is stopped and is later resumed if the user
navigates back.
Here are some points that need to be taken care while creating fragments.
Check the backstack if the fragment is already created.
If it was created previously pop it from the backstack and put it on top so that it is visible to the user.
If fragment is not present in backstack crate it and store it in backstack.
Create a method like below which will handle such situation.
private void openFragment(Fragment fragment_to_be_opened){
String fragment_to_be_opened_name = fragment_to_be_opened.getClass().getName();
FragmentManager manager = getSupportFragmentManager();
// fetching the fragment if it is present in backstack
boolean fragment_allready_present = manager.popBackStackImmediate (fragment_to_be_opened_name, 0);
if (!fragment_allready_present){ //fragment is not present in backstack so create it and save the name in //backstack
FragmentTransaction fragment_trasition = manager.beginTransaction();
fragment_trasition.replace(R.id.fragment_container, fragment_to_be_opened);
fragment_trasition.addToBackStack(fragment_to_be_opened_name);
fragment_trasition.commit();
}
}
Now call this method from the activity to open a new fragment like
// create instance of fragment and pass it to the open fragment method
Fragment myFragment = new myFragment();
openFragment(myFragment);
Having a flow to add fragment and later replace() fragment. All fragment are dynamically added, but not all call addToBackStack().
getSupportFragmentManager().beginTransaction()
.add(R.id.frgment_holder, frgmtA, frgmtA.NAME)
.commit();
and in somewhere it could add another, like:
getSupportFragmentManager().beginTransaction()
.replace(R.id.frgment_holder, frgmtB)
.addToBackStack(frgmtB.NAME)
.commit();
the replace() with the frgmtB will remove the frgmtA from the container R.id.frgment_holder.
if press back button at this state, it will pop the frgmtB. But will it recreate the frgmtA even if it did not call addToBackStack() when did the adding?
if in the flow of adding fragment into same container with a serials mixed add() and replace() calls, and someone call addToBackStack() but someone do not, how would the back button behavior?
EDIT:
after
getSupportFragmentManager().beginTransaction()
.replace(R.id.frgment_holder, frgmtB)
.addToBackStack(frgmtB.NAME)
.commit();
will the
getSupportFragmentManager().findFragmentByTag(frgmtA.NAME);
still find the the frgmtA? what if when add frgmtA also called addToBackStack();
The doc says "This first searches through fragments that are currently added to the manager's activity; if no such fragment is found, then all fragments currently on the back stack are searched."
The case would be
adding frgmtA; not call add to stack; UI state changed here;
(what if the frgmtA is not dynamically added by add(), but sepcified in layout file with class="frgmtA"?).
replace() with frgmtB; addToStack();
replace() with frgmtC; addToStack();
then if the stackTop is frgmtC, would like the back button press to bring back the first frgmtA with its last UI state.
1.
.add(R.id.frgment_holder, frgmtA, frgmtA.NAME)
.commit();
.replace(R.id.frgment_holder, frgmtB, frgmtB.NAME)
.addToBackStack(frgmtB.NAME)
.commit();`
the replace will remove the frgmtA from the the holder and its onDestroyView will be called (but since it is referenced in the backstack’s transaction data, frgmtA is NOT destroyed). And the frgmtB will be added to the holder.
Since the frgmtA is not destroyed, the
getSupportFragmentManager().findFragmentByTag(frgmtA.NAME);
will find it.
after then, press on back button, it will pop the top transaction from the backStack, then reversing the transaction. i.e. remove frgmtB from the holder and add frgmtA back to the holder. Since there is no more reference to frgmtB its onDestroy is called.
2.
in the case of
add frgmtA;
replace() with frgmtB; addToStack();
replace() with frgmtC; addToStack();
If want to back press to jump to frgmtA, need to override onBackPressed(), in it
do
popBackStack(frgmtB.NAME, POP_BACK_STACK_INCLUSIVE),
which will toss out the transaction in the stack above stack entry named frgmtB.NAME and do reverse the transaction on it, which will add back the frgmtA in the holder.
Unless you interfere with the Back button logic using onBackPressed() or addOnBackStackChangedListener(), things are straight forward
if you add a fragment through addToBackStack(), it will be removed when Back is tapped
if there are no other fragments left in the back stack, app will be closed
try this anyone line as your demand
its 4 differnt lines code you have checked
getActivity().getSupportFragmentManager().popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
getActivity().getSupportFragmentManager().popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
FragmentTransaction transaction = getActivity().getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.framelayout, fragment);
transaction.addToBackStack(null);
getActivity().getSupportFragmentManager().popBackStack();
I am using a navigation drawer and fragments to inflate the main activity.
However when I press the back button it closes the application which is obvious with fragment setup.
What I want is that when the user clicks the back button they go to the home page fragment if its not currently the view, but if they are on the home page I want the app to close.
How to go around this problem?
Overriding the back button would be a REALLY BAD idea, as it will hurt more than help.
If you want to add a Fragment that will not be replaced (Your initial "HomePageFragment"), just add this line:
transaction.addToBackStack(null);
Which will give you something like:
final FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.fragment, fragment, TAG_FRAGMENT);
transaction.addToBackStack(null);
transaction.commit();
If you add some other Fragments, don't add them to the back stack.
You should override the onBackPressed method in the activity class and using that you can determine whether you need to switch back to your home fragment or call super.onBackPressed().
I add, show and hide fragments. Each time I add/show a fragment, I hide the previous fragment and add the transaction to the backstack.
When a user presses the back button, a fragment is popped and I would like to have a reference to it.
Why do I need a reference? So I could hide it when the user continues to the next fragment.
So, how do I get a reference to a popped fragment?
EDIT-25-04-2013:
Here's code to explain how to add a new fragment, while hiding the previous one. The question is how to get a reference to the last fragment after it is popped from the backstack (using the back button)?
FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
fragmentTransaction.hide(lastFragment);
fragmentTransaction.add(newFragment);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
lastFragment = newFragment;
I use generated tags for each fragment, save the tags in a stack and persist the stack. This way I get hold of every fragment out there - last one in specific.
See the code 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.