My app has 3 UI levels, each level has its own fragment, A -> B -> C.
I wish to optionally allow my app user to navigate straight to the top level fragment i.e. from C -> A without invoking B.
i.e., I still want to allow the user to go from C->B if they press the back button, but in the C fragment, I have a "Home" button, which takes them directly to A. This is the operation where I want to flush the backstack.
Additionally, I want the user to be able to go from B->A using the back button, hence I'm adding A and B both to the backstack.
I have tried the options from this SO post but in each case, onCreateView gets invoked for each fragment, even if I use fm_.popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
Also checked this thread, it seems to recommend using popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE)
Can anyone please suggest a way to pop the entire backstack without having the onCreateView called on popped fragments?
Dont add backStack line for going from fragment B->C
while going from A->B use
HighlightFragment highlightFragment=new HighlightFragment(FirstReaderScreen.this);
getSupportFragmentManager()
.beginTransaction()
.add(R.id.LL_Fragment, highlightFragment) // LL_Fragment is container
.addToBackStack(null)
.commit();
while going B->C use
HighlightFragment highlightFragment=new HighlightFragment(FirstReaderScreen.this);
getSupportFragmentManager()
.beginTransaction()
.add(R.id.LL_Fragment, highlightFragment)
.commit();
dont add line .addtobackstack(null)
This worked for me
i estimate you're using replace fragment.
try to use begintransaction.hide / show to manage many fragment inside 1 activity if you don want to popback
//on Activity
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(containerId, new FragmentA(), fragmentA.getClass().getSimpleName());
fragmentTransaction.commit();
//inside fragment A to jump fragment without PopBackStack
getFragmentManager().beginTransaction().hide(this).commit();
getFragmentManager().beginTransaction().show(new FragmentB()).commit();
This is working for me when needing to clear the back stack for tab switches in BottomNavigationView
public void clearBackStack() {
int backStackEntryCount = mFragmentActivity
.getSupportFragmentManager()
.getBackStackEntryCount();
for (int i = 0; i < backStackEntryCount; i++) {
mFragmentActivity.getSupportFragmentManager().popBackStack();
}
}
Related
MainFragment launches FragA that launches FragB that lunches FragC.
Back button press on FragB should go to FragA and back press on FragC should also go to FragA.
FragB is the only one where isToAddToBackStack is false.
childFragmentManager.commit {
replace(containerViewId, fragment, fragment::class.java.name)
if (isToAddToBackStack) {
addToBackStack(backStateName)
}
}
Whenever the back button is pressed on FragC, FragB is shown:
if (childFragmentManager.backStackEntryCount > 1) {
childFragmentManager.popBackStack()
return
}
What's the better way to achieve the navigation that I'm expecting?
Fragment transactions can involve two different types of tags. The one that most Android developers are familiar with is the Fragment tag, which you can use to find a specific Fragment in your FragmentManager later via findFragmentByTag(). This is useful for finding a Fragment when your application is in a particular state, but keep in mind that the Fragment needs to be added to your FragmentManager. If you have removed() or replaced() a Fragment and haven’t added it to the back stack, you won’t be able to find it.
The other type of tag is the BackStackRecord’s name passed into addToBackStack(). This name identifies a particular back stack record, which is a record of what occurred in a particular transaction. popBackStackImmediate() and its counterparts have variants that accept a back stack record name to pop the back stack to a particular state.
//fm is FragmentManager
// Fragment a is on the screen
Fragment a = new A_Fragment()
fm.beginTransaction()
.remove(null /*no fragments in R.id.content*/)
.add(R.id.content, aFragment, "fragment-a")
.commit();
// user wants to go from A to B
Fragment bFragment = new B_Fragment();
fm.beginTransaction()
.remove(fm.findFragmentById(R.id.content))
.add(R.id.content, bFragment, "fragment-b")
.addToBackStack("a")
.commit();
// user wants to go from B to C
fm.beginTransaction()
.remove(fm.findFragmentById(R.id.content))
.add(R.id.content, new C_Fragment(), "fragment-c")
.commit();
The project is multiple-fragment project. Launch application to start BaseActivity, which all fragments are attached into it.
Issue:
Fragment A is the first fragment and is launched very well.
Fragment B is trigged by clicking button of Fragment A, also launched very well.
During from B back to A, the problem occurs. Nothing to display, only background, the BaseActivity.
All fragments use the api "replace" to display, although this API may produce performance problems, please ignore it firstly.
I have tried many methods, but nothing works.
The launched fragment method shows below:
public void navigateToFragment(Fragment fragment, boolean isClear){
if(isClear){
getSupportFragmentManager()
.popBackStack(null,FragmentManager.POP_BACK_STACK_INCLUSIVE);
}
getSupportFragmentManager().beginTransaction()
.addToBackStack(null)
.replace(R.id.container,fragment)
.commit()
}
As I know, popBackStack and replace, commit can lead to one fragment is launched twice, but why no display after replace whatever I replace it?
try use add instead of replace
getSupportFragmentManager().beginTransaction()
.addToBackStack(null)
.add(R.id.container,fragment)
.commit()
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 have a problem that I've been dealing with for the last couple o days and don't seem to find an answer to it.
Description : I have a main activity which contains a navigation drawer. Each item of the navigation drawer (when clicked) creates a fragment. In that fragment, there is a listView of objects, which creates other fragments when clicked. In those fragments i have another listView of objects which opens other fragments. In other words, there series of fragment that open other fragment. Something like this:
http://s22.postimg.org/pddo5gsv5/backstack.png
In order to be able to get back to each fragment, I've implemented the addToBackstack("string") method.
My question is, how can I implement correct backstack for my application so that when i click a navigation Drawer item, all the fragments that have been added to backstack are cleared, without the one that the navigation Drawer item opens.
Any help would be appreciated. Thank you !
EDIT
Ok, it seems I managed to figure it out. Considering what advices i received from the replies, here's the solution I came up with:
#Override
public void onBackPressed() {
int count = getFragmentManager().getBackStackEntryCount();
if (count != 0) {
FragmentManager.BackStackEntry backEntry = getFragmentManager()
.getBackStackEntryAt(
getFragmentManager().getBackStackEntryCount() - 1);
if (backEntry.getName() == NAVIGATION) {
finish();
} else
getFragmentManager().popBackStack();
} else {
super.onBackPressed();
}
}
To put it in words: First, i added a backstack entry even for the top level fragments, given them a specific tag. The I have overridden the Activity's back button function so that when the last backstack entry is a top-level fragment to finish the activity (so that it not simply detach the fragment from activity, living it empty). Otherwise, if the last entry isn't an top-level fragment, execute a popBackStack.
PS: All non-top-level fragments are added to the backstack with a different tag then the top-level one. Also, i had to do a POP_BACK_STACK_INCLUSIVE in the navigation Drawer's click listener.
getFragmentManager().popBackStack(null,
FragmentManager.POP_BACK_STACK_INCLUSIVE);
Thank you all for the advices and hopefully this EDIT help other users.
You can use the following code to solve your problem:
getFragmentManager().beginTransaction()
.replace(R.id.fragment_container, fragment)
.addToBackStack(fragment_tag)
.commit();
In order to make the code above work, you have to create the fragments dynamically. As hardcoded fragments cannot be replaced. To do that, you can create a container (FrameLayout etc.) which in our example has the id fragment_container. Then, the code above will add the fragment in the container dynamically. Finally, you have to pass as parameter in the addToBackStack method the fragment_tag. That means, that this transaction will be added in the back stack. And finally, in order to get it from the backstack you have to use the code below:
getFragmentManager().popBackStack(fragment_tag, FragmentManager.POP_BACK_STACK_INCLUSIVE));
The POP_BACK_STACK_INCLUSIVE flag, insures that "all matching entries will be consumed until one that doesn't match is found or the bottom of the stack is reached. Otherwise, all entries up to but not including that entry will be removed."
You can clear the fragment backstack by using something like:
fragmentManager.popBackStack("string", FragmentManager.POP_BACK_STACK_INCLUSIVE);
and then you can addToBackstack and commit as usual. More info.
A code snippet that shows the way I normally use it in navigation drawers:
FragmentManager fragmentManager = getSupportFragmentManager();
if(clearBackStack) {
fragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
}
FragmentTransaction ft = fragmentManager.beginTransaction();
ft.replace(R.id.content_frame, fragment);
if(!clearBackStack) {
ft.addToBackStack(null);
}
ft.commit();
My Application has one Activity with a layout (FrameLayout) for many fragments.
At the start of the Application it displays a list of Places (PlacesFragment).
When a Place is clicked, a Fragment that displays a list of cams (CamFragment) is added. Inside this fragment there is also a button "info".
When the user press the "info" button a new fragment (InfoPlaceFragment) that displays information about the place, is added.
Here is a graphical explanation:
I want to be able to go back to "CamFragment(B)" from "InfoPlaceFragment(C)", and to go back to "PlacesFragment(A)" from "CamFragment(B)".
One Question:
1) Is it possible to realize this or an alternative solution is better?
------- EDIT -------
SOLUTION:
In the "MainActivity.java":
onPlaceParse() // This method is called when the data from the server is received:
// Here I create the "PlaceFragment"
fManager = getSupportFragmentManager();
fragmentPlaces = new PlacesFragment();
fManager.beginTransaction()
.replace(R.id.fragment_list_container, fragmentPlaces)
.commit();
onResortSelected // This method is called when the resort is selected
fragmentCams = new CamsFragment();
fManager.beginTransaction()
.replace(R.id.fragment_list_container, fragmentCams)
.addToBackStack(null)
.commit();
onButtonInfoSelected // This method is called when the btn info is selected
fragmentInfo = new InfoResortFragment();
fManager = getSupportFragmentManager();
fManager.beginTransaction()
.replace(R.id.fragment_list_container, fragmentInfo, "Info")
.addToBackStack(null)
.commit();
When you press the back button, if you are in the "Info Fragment" it returns to "PlaceFragment" and if you are in the "CamsFragment" it returns to "PlacesFragment".
This is possible, you need to call addToBackStack(null) in the fragment transactions.
Reference: Providing Proper Back Navigation
When you create your FragmentTransaction that will add/remove/replace (or whatever pattern you are using) the Fragments, just make sure to call addToBackStack(). Then, when the user presses the back button, the system will automatically cycle back through the entries added to the back stack, in reverse order from how they were originally added. No additional work is required on your part! :)