Problems with Android Fragment back stack - android

I've got a massive problem with the way the android fragment backstack seems to work and would be most grateful for any help that is offered.
Imagine you have 3 Fragments
[1] [2] [3]
I want the user to be able to navigate [1] > [2] > [3] but on the way back (pressing back button) [3] > [1].
As I would have imagined this would be accomplished by not calling addToBackStack(..) when creating the transaction that brings fragment [2] into the fragment holder defined in XML.
The reality of this seems as though that if I dont want [2] to appear again when user presses back button on [3], I must not call addToBackStack in the transaction that shows fragment [3]. This seems completely counter-intuitive (perhaps coming from the iOS world).
Anyway if i do it this way, when I go from [1] > [2] and press back I arrive back at [1] as expected.
If I go [1] > [2] > [3] and then press back I jump back to [1] (as expected).
Now the strange behavior happens when I try and jump to [2] again from [1]. First of all [3] is briefly displayed before [2] comes into view. If I press back at this point [3] is displayed, and if I press back once again the app exits.
Can anyone help me to understand whats going on here?
And here is the layout xml file for my main activity:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<fragment
android:id="#+id/headerFragment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
class="com.fragment_test.FragmentControls" >
<!-- Preview: layout=#layout/details -->
</fragment>
<FrameLayout
android:id="#+id/detailFragment"
android:layout_width="match_parent"
android:layout_height="fill_parent"
/>
Update
This is the code I'm using to build by nav heirarchy
Fragment frag;
FragmentTransaction transaction;
//Create The first fragment [1], add it to the view, BUT Dont add the transaction to the backstack
frag = new Fragment1();
transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.detailFragment, frag);
transaction.commit();
//Create the second [2] fragment, add it to the view and add the transaction that replaces the first fragment to the backstack
frag = new Fragment2();
transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.detailFragment, frag);
transaction.addToBackStack(null);
transaction.commit();
//Create third fragment, Dont add this transaction to the backstack, because we dont want to go back to [2]
frag = new Fragment3();
transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.detailFragment, frag);
transaction.commit();
//END OF SETUP CODE-------------------------
//NOW:
//Press back once and then issue the following code:
frag = new Fragment2();
transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.detailFragment, frag);
transaction.addToBackStack(null);
transaction.commit();
//Now press back again and you end up at fragment [3] not [1]
Many thanks

Explanation: on what's going on here?
If we keep in mind that .replace() is equal with .remove().add() that we know by the documentation:
Replace an existing fragment that was added to a container. This is essentially the same as calling remove(Fragment) for all currently added fragments that were added with the same containerViewId and then add(int, Fragment, String) with the same arguments given here.
then what's happening is like this (I'm adding numbers to the frag to make it more clear):
// transaction.replace(R.id.detailFragment, frag1);
Transaction.remove(null).add(frag1) // frag1 on view
// transaction.replace(R.id.detailFragment, frag2).addToBackStack(null);
Transaction.remove(frag1).add(frag2).addToBackStack(null) // frag2 on view
// transaction.replace(R.id.detailFragment, frag3);
Transaction.remove(frag2).add(frag3) // frag3 on view
(here all misleading stuff starts to happen)
Remember that .addToBackStack() is saving only transaction not the fragment as itself! So now we have frag3 on the layout:
< press back button >
// System pops the back stack and find the following saved back entry to be reversed:
// [Transaction.remove(frag1).add(frag2)]
// so the system makes that transaction backward!!!
// tries to remove frag2 (is not there, so it ignores) and re-add(frag1)
// make notice that system doesn't realise that there's a frag3 and does nothing with it
// so it still there attached to view
Transaction.remove(null).add(frag1) //frag1, frag3 on view (OVERLAPPING)
// transaction.replace(R.id.detailFragment, frag2).addToBackStack(null);
Transaction.remove(frag3).add(frag2).addToBackStack(null) //frag2 on view
< press back button >
// system makes saved transaction backward
Transaction.remove(frag2).add(frag3) //frag3 on view
< press back button >
// no more entries in BackStack
< app exits >
Possible solution
Consider implementing FragmentManager.BackStackChangedListener to watch for changes in the back stack and apply your logic in onBackStackChanged() methode:
Trace a count of transaction;
Check particular transaction by name FragmentTransaction.addToBackStack(String name);
Etc.

It seems as though fragment [3] is not removed from the view when back is pressed so you have to do it manually!
First of all, don't use replace() but instead use remove and add separately. It seems as though replace() doesn't work properly.
The next part to this is overriding the onKeyDown method and remove the current fragment every time the back button is pressed.
#Override
public boolean onKeyDown(int keyCode, KeyEvent event)
{
if (keyCode == KeyEvent.KEYCODE_BACK)
{
if (getSupportFragmentManager().getBackStackEntryCount() == 0)
{
this.finish();
return false;
}
else
{
getSupportFragmentManager().popBackStack();
removeCurrentFragment();
return false;
}
}
return super.onKeyDown(keyCode, event);
}
public void removeCurrentFragment()
{
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
Fragment currentFrag = getSupportFragmentManager().findFragmentById(R.id.detailFragment);
String fragName = "NONE";
if (currentFrag!=null)
fragName = currentFrag.getClass().getSimpleName();
if (currentFrag != null)
transaction.remove(currentFrag);
transaction.commit();
}

First of all thanks #Arvis for an eye opening explanation.
I prefer different solution to the accepted answer here for this problem. I don't like messing with overriding back behavior any more than absolutely necessary and when I've tried adding and removing fragments on my own without default back stack poping when back button is pressed I found my self in fragment hell :) If you .add f2 over f1 when you remove it f1 won't call any of callback methods like onResume, onStart etc. and that can be very unfortunate.
Anyhow this is how I do it:
Currently on display is only fragment f1.
f1 -> f2
Fragment2 f2 = new Fragment2();
this.getActivity().getSupportFragmentManager().beginTransaction().replace(R.id.main_content,f2).addToBackStack(null).commit();
nothing out of the ordinary here. Than in fragment f2 this code takes you to fragment f3.
f2 -> f3
Fragment3 f3 = new Fragment3();
getActivity().getSupportFragmentManager().popBackStack();
getActivity().getSupportFragmentManager().beginTransaction().replace(R.id.main_content, f3).addToBackStack(null).commit();
I'm not sure by reading docs if this should work, this poping transaction method is said to be asynchronous, and maybe a better way would be to call popBackStackImmediate(). But as far I can tell on my devices it's working flawlessly.
The said alternative would be:
final FragmentActivity activity = getActivity();
activity.getSupportFragmentManager().popBackStackImmediate();
activity.getSupportFragmentManager().beginTransaction().replace(R.id.main_content, f3).addToBackStack(null).commit();
Here there will actually be brief going back to f1 beofre moving on to f3, so a slight glitch there.
This is actually all you have to do, no need to override back stack behavior...

I know it's a old quetion but i got the same problem and fix it like this:
First, Add Fragment1 to BackStack with a name (e.g "Frag1"):
frag = new Fragment1();
transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.detailFragment, frag);
transaction.addToBackStack("Frag1");
transaction.commit();
And then, Whenever you want to go back to Fragment1 (even after adding 10 fragments above it), just call popBackStackImmediate with the name:
getSupportFragmentManager().popBackStackImmediate("Frag1", 0);
Hope it will help someone :)

After #Arvis reply i decided to dig even deeper and I've written a tech article about this here: http://www.andreabaccega.com/blog/2015/08/16/how-to-avoid-fragments-overlapping-due-to-backstack-nightmare-in-android/
For the lazy developers around. My solution consists in always adding the transactions to the backstack and perform an extra FragmentManager.popBackStackImmediate() when needed (automatically).
The code is very few lines of code and, in my example, I wanted to skip from C to A without jumping back to "B" if the user didn't went deeper in the backstack (ex from C navigates to D).
Hence the code attached would work as follow
A -> B -> C (back) -> A
&
A -> B -> C -> D (back) -> C (back) -> B (back) -> A
where
fm.beginTransaction().replace(R.id.content, new CFragment()).commit()
were issued from "B" to "C" as in the question.
Ok,Ok here is the code :)
public static void performNoBackStackTransaction(FragmentManager fragmentManager, String tag, Fragment fragment) {
final int newBackStackLength = fragmentManager.getBackStackEntryCount() +1;
fragmentManager.beginTransaction()
.replace(R.id.content, fragment, tag)
.addToBackStack(tag)
.commit();
fragmentManager.addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
#Override
public void onBackStackChanged() {
int nowCount = fragmentManager.getBackStackEntryCount();
if (newBackStackLength != nowCount) {
// we don't really care if going back or forward. we already performed the logic here.
fragmentManager.removeOnBackStackChangedListener(this);
if ( newBackStackLength > nowCount ) { // user pressed back
fragmentManager.popBackStackImmediate();
}
}
}
});
}

If you are Struggling with addToBackStack() & popBackStack() then simply use
FragmentTransaction ft =getSupportFragmentManager().beginTransaction();
ft.replace(R.id.content_frame, new HomeFragment(), "Home");
ft.commit();`
In your Activity In OnBackPressed() find out fargment by tag and then do your stuff
Fragment home = getSupportFragmentManager().findFragmentByTag("Home");
if (home instanceof HomeFragment && home.isVisible()) {
// do you stuff
}
For more Information https://github.com/DattaHujare/NavigationDrawer
I never use addToBackStack() for handling fragment.

I think, when I read your story that [3] is also on the backstack. This explains why you see it flashing up.
Solution would be to never set [3] on the stack.

I had a similar issue where I had 3 consecutive fragments in the same Activity [M1.F0]->[M1.F1]->[M1.F2] followed by a call to a new Activity[M2]. If the user pressed a button in [M2] I wanted to return to [M1,F1] instead of [M1,F2] which is what back press behavior already did.
In order to accomplish this I remove [M1,F2], call show on [M1,F1], commit the transaction, and then add [M1,F2] back by calling it with hide. This removed the extra back press that would have otherwise been left behind.
// Remove [M1.F2] to avoid having an extra entry on back press when returning from M2
final FragmentTransaction ftA = fm.beginTransaction();
ftA.remove(M1F2Fragment);
ftA.show(M1F1Fragment);
ftA.commit();
final FragmentTransaction ftB = fm.beginTransaction();
ftB.hide(M1F2Fragment);
ftB.commit();
Hi
After doing this code: I'm not able to see value of Fragment2 on pressing Back Key.
My Code:
FragmentTransaction ft = fm.beginTransaction();
ft.add(R.id.frame, f1);
ft.remove(f1);
ft.add(R.id.frame, f2);
ft.addToBackStack(null);
ft.remove(f2);
ft.add(R.id.frame, f3);
ft.commit();
#Override
public boolean onKeyDown(int keyCode, KeyEvent event){
if(keyCode == KeyEvent.KEYCODE_BACK){
Fragment currentFrag = getFragmentManager().findFragmentById(R.id.frame);
FragmentTransaction transaction = getFragmentManager().beginTransaction();
if(currentFrag != null){
String name = currentFrag.getClass().getName();
}
if(getFragmentManager().getBackStackEntryCount() == 0){
}
else{
getFragmentManager().popBackStack();
removeCurrentFragment();
}
}
return super.onKeyDown(keyCode, event);
}
public void removeCurrentFragment()
{
FragmentTransaction transaction = getFragmentManager().beginTransaction();
Fragment currentFrag = getFragmentManager().findFragmentById(R.id.frame);
if(currentFrag != null){
transaction.remove(currentFrag);
}
transaction.commit();
}

executePendingTransactions() , commitNow() not worked (
Worked in androidx (jetpack).
private final FragmentManager fragmentManager = getSupportFragmentManager();
public void removeFragment(FragmentTag tag) {
Fragment fragmentRemove = fragmentManager.findFragmentByTag(tag.toString());
if (fragmentRemove != null) {
fragmentManager.beginTransaction()
.remove(fragmentRemove)
.commit();
// fix by #Ogbe
fragmentManager.popBackStackImmediate(tag.toString(),
FragmentManager.POP_BACK_STACK_INCLUSIVE);
}
}

Related

Android reload fragments stack on restore

I’m developing an android application that makes heavy use of fragments, I’m running into an issue and I’ve been unable to find a solution so far.
The flow is this: the app is launched and MainActivity is the first responder, now, depending on user interaction several fragments gets loaded and pushed onto the stack.
Here is an example:
Main Activity -> fragment A -> fragment B -> fragment C -> etc..
Back history is enabled like so:
fragment C -> fragment B -> fragment A -> etc..
Everything works perfectly fine as long as my application is in foreground but everything breaks when the application goes in background.
If I’m on fragment B for example and I press the home button the application goes in background and when I restore it back it starts from MainActivity with fragment A.
Also, the toolbar shows the title of fragment B and, since fragment A contains a recyclerview I can see parts of fragment B between item rows, like a background image.
This is how I load fragments:
public void loadFragment(Fragment fragment, Boolean addToStack) {
// Load fragment
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.frame_container, fragment);
fragmentTransaction.addToBackStack(null);
// show back button
if (addToStack) {
// Code to show the back button.
}
else if (fragmentManager.getBackStackEntryCount() > 0 && !addToStack) {
hideBackButton();
fragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
}
fragmentTransaction.commitAllowingStateLoss();
}
I call this function from MainActivity and from the fragments:
MyFragment theFragment = new MyFragment();
MainActivity.instance.loadFragment(theFragment, true);
What I want to achieve is that when the application is restored it gets straight to the previously loaded fragment, keeping the entire "back" history. How can I do this?
I'm not sure if other portions of code are needed, but if so I'll post them as required.
call the onResume() function:
public void onResume(){
Fragment frg = null;
frg = getSupportFragmentManager().findFragmentByTag("Your_Fragment_TAG");
final FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.detach(frg);
ft.attach(frg);
ft.commit();
}
Ok so, after a lot of research I found the issue… and the issue was a mistake of mine.
I’d like to report it here for everyone that might run into the same issue.
At first I tried to force fragment replacement on onResume() function like so:
Fragment f = getSupportFragmentManager().findFragmentById(R.id.container);
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.frame_container, f);
fragmentTransaction.addToBackStack(null);
However android should handle all of this automatically, at least in my knowledge, and so I tried to dig further and I finally narrowed it down to my onStart() method.
Basically I was registering the EventBus and making a function call
if (!EventBus.getDefault().isRegistered(this)) {
EventBus.getDefault().register(this);
UserNetworkManager userNetworkManager = new UserNetworkManager(MainActivity.mainActivity);
userNetworkManager.fetchFeed();
}
This code was creating the issue and after all it was not necessary to put it there, so I moved it to the onCreate() method, cleaned up my code a little bit and everything works fine now.

Fragments and backstage sometimes get messed up

I have an activity with a container for fragments and a NavigationDrawer. If I select an item in the drawer, I call updatePage(index).
I also call updatePage(0) if I create the activity and the savedInstanceState == null to init my activity.
One fragment has a sub fragment and therefor it just replaces itself by the subfragment and adds the subfragment to the backstack, so that the user can navigate back to the previous fragment. In this fragment I call following code directly:
#Override
public void onClick(View view)
{
Event event = (Event)view.getTag();
FragmentManager fm = getActivity().getSupportFragmentManager();
GamesFragment f = new GamesFragment();
Bundle bundle = new Bundle();
bundle.putParcelable(GamesFragment.KEY_EVENT, event);
f.setArguments(bundle);
FragmentTransaction ft = fm.beginTransaction()
.replace(R.id.frame_container, f, f.getClass().getName())
.addToBackStack(null);
ft.commit();
}
Why does sometimes the removing of the old fragment not work? I get overlaying fragments, but only sometimes.
My activities updatePage function looks like following:
private void updatePage(int drawerSelection)
{
mDrawer.closeDrawer();
Fragment f = null;
FragmentManager fm = getSupportFragmentManager();
switch (drawerSelection)
{
case 0:
f = fm.findFragmentByTag(HomeFragment.class.getName());
if (f == null)
f = new HomeFragment();
break;
case 1:
f = fm.findFragmentByTag(EventFragment.class.getName());
if (f == null)
f = new EventFragment();
break;
default:
break;
}
if (f != null && !f.isAdded())
{
// SOLUTION:
// Backstack clearen
// fm.popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
FragmentTransaction ft = fm.beginTransaction();
ft.replace(R.id.frame_container, f, f.getClass().getName());
ft.commit();
}
}
I saw such situation few times and I bet that it can happen after replacing with addToBackStack. Easiest fix is to add background to fragment layout. From android doc:
Note: When you remove or replace a fragment and add the transaction to the back stack, the fragment that is removed is stopped (not destroyed). If the user navigates back to restore the fragment, it restarts. If you do not add the transaction to the back stack, then the fragment is destroyed when removed or replaced.
This view is not redrawed entirely just overdrawed and if second fragment doesn't has background or view container redraw you will get effect as described. That's my theory :>. Sometimes Android documentation is not as clear as we would like it to be.
The problem is following use case:
Going to the EventFragment from the GameFragment pushes the "remove(EventFragment).add(GameFragment)" transaction to the backstack. If I now press the back button, this transaction will be undone and everything is fine. The backstack is empty again and everything works. BUT, if I don't press the back button, but change to another fragment through the menu, the backstack does still have the above mentioned transaction. Pressing back will now try to undo this transaction... It will just readd the EventFragment before the code in my menu click handler adds the fragment from the menu... This is how it could happen...
Easy solution, if I only want one backstack for every menu entry and want to delete the backstack, if I select another area of my app in the menu is, to clear the backstack before I go to another area through the menu...
So adding ' fm.popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);' before replacing the the current fragment will solve the problem (I added this solution as a comment to my main post)

How to back on Fragments without FragmentTransaction?

I have 4 fragment in my android app :
let's say A B C D where they 4 are fragments called in 1 activity. When I go from A to B and from be to C I want the back button to bring me to B and to A if I press again. But instead the back button forwards me to the previous activity.
I've read that the back button need to explicitely be handled as for fragments and I saw that code :
FragmentTransaction tx = fragmentManager.beginTransation();
tx.replace( R.id.fragment, new MyFragment() ).addToBackStack( "tag" ).commit();
fragment.getView().setFocusableInTouchMode(true);
fragment.getView().setOnKeyListener( new OnKeyListener()
{
#Override
public boolean onKey( View v, int keyCode, KeyEvent event )
{
if( keyCode == KeyEvent.KEYCODE_BACK )
{
return true;
}
return false;
}
} );
The problem is that I'm not using any FragmentTransaction. Here is how I call my Fragments :
private void displayView(int position) {
Fragment fragment = null;
switch (position) {
case 0:
fragment = new HomeFragment();
break;
/*case 1:
fragment = new ContactsFragment();
break;*/
case 2:
fragment = new ContactsFragment();
break;
case 3:
fragment = new SitesFragment();
break;
default:
break;
}
if (fragment != null) {
FragmentManager fragmentManager = getFragmentManager();
fragmentManager.beginTransaction()
.replace(R.id.frame_container, fragment).commit();
}
How can I use that back stacking with my code please ?
Thank you for your help in advance !
By calling .beginTransaction(), you are actually using a FragmentTransaction. The type itself is just not explicitly mentioned in your example. You can simply call addToBackStack() on the transaction to add the transaction to the fragment manager's back stack:
fragmentManager.beginTransaction()
.replace(R.id.frame_container, fragment).addToBackStack(null).commit();
Edit:
As Larry pointed out, adding to the fragment manager's back stack will automatically handle going back if your structure is as simple as A>B>C>D. If, for any reason, you need to manually handle the behavior of popping the backstack, you can use these instructions below:
In your activity, override onBackPressed() and call getFragmentManager().popBackStack(); Note: that you will need to know which fragment you are currently displaying so that you can safely call popBackStack on B, C, and D but if you are on A, you will want to (most likely) exit the activity. (Alternatively, you could only pop the back stack if there are items in it. For that, check if (getFragmentManager().getBackStackEntryCount() > 0)
Here's how your full fragment transaction code should look:
// the tag here is if you need to know which fragment is loaded, later
fragmentManager.beginTransaction()
.replace(R.id.frame_container, fragment, tag).addToBackStack(null).commit();
Then, the back handler:
#Override
public void onBackPressed()
{
if (someCriteriaToDetermineIfBackStackShouldBePopped)
{
getFragmentManager().popBackStack();
return;
}
// this fragment is A (or something else) - let the parent handle the back press
// which will finish the activity
super.onBackPressed();
}
If you need help detecting which fragment is currently loaded, this should get you started: get currently displayed fragment
You need to use the FragmentTransaction to add/replace the new fragment, add it to the back stack, then commit. You do not need to handle the BACK button yourself unless you wish to override the default behavior. Adding it to the back stack in the FragmentTrasnaction before you commit will automatically pick up the desired BACK button support.

Going from 3rd to 1st fragment in the backstack: Android

I have a sequence of event via which i have added three fragments to the backstack, one by one. Each of these fragments covers the full screen of the activity.
I have stored the is returned from the commit of Frag1.
Now in Frag3, based on a specific click, I want to go back to Frag1 directly and discard/pop all Fragments in between.
So, when this button is clicked i send a message to the activity which does the following:
getSupportFragmentManager().popBackStack(mFrag1Id, FragmentManager.POP_BACK_STACK_INCLUSIVE);
But i just got a blank screen, so i assume no fragment was loaded.
I even tried:
In commit - fragmentTransaction.addToBackStack("Fragment1");
and then
getSupportFragmentManager().popBackStack("Fragment1", FragmentManager.POP_BACK_STACK_INCLUSIVE);
But it doesn't work.
Could someone please help me with this?
Thanks.
OK so I found the issue.
FragmentManager.POP_BACK_STACK_INCLUSIVE pops all the fragments including the one whose id passed as argument.
SO for example:
getSupportFragmentManager().popBackStack(mFrag1Id, FragmentManager.POP_BACK_STACK_INCLUSIVE);
Here it will pop everything on the stack including fragment whose id id mFrag1Id.
from third fragment you should call popBackStack();
twice (one to remove third fragment and the second to remove second fragment )
android.support.v4.app.FragmentManager fm = getActivity().getSupportFragmentManager();
FragmentTransaction transaction = fm.beginTransaction();
transaction.remove(ThirdFragment.this);
transaction.commit();
fm.popBackStack();
fm.popBackStack();
When you opened Fragment A and you Navigated to Fragment B and then to Fragment C and then You want to close Fragment C and B and land on Fragment A
Now in some scenario, you want to close Fragment C and Fragment B and you want to land on Fragment A... then use this logic of FragmentManager to do such task.
First get the number of fragment entries in back stack (When we are adding any fragment to addToBackStack("Frag1")) at that time fragment back stack entry will increase.
so get using this
FragmentManager fmManager = activity.getSupportFragmentManager();
Log.e("Total Back stack Entry: ", fmManager.getBackStackEntryCount() + "");
Now assume, you want to close current fragment (Fragment C) and your last fragment (Fragment B) so simple logic is getBackStackEntryCount -2 and at that time your back stack entry count will be 3 (Fragment A, Fragment B and Fragment C)
Here -2 is for because we want to go 2 fragment step back (Fragment C
and Fragment B)
So simple two line of Code is:
if (fmManager.getBackStackEntryCount() > 0) {
fmManager.popBackStack(fmManager.getBackStackEntryAt(fmManager.getBackStackEntryCount()-2).getId(), FragmentMaanger.POP_BACK_STACK_INCLUSIVE);
}
You can also do it by adding two time "popBackStack()" and will also work, but it not idle way to do this
FragmentManager fmManager = activity.getSupportFragmentManager();
fmManager.popBackStack();
fmManager.popBackStack();
If you want user to back at the beginning fragment, code snippet below will help you.
public static void popBackStackInclusive(AppCompatActivity activity) {
FragmentManager fragmentManager = activity.getSupportFragmentManager();
for (int i = 1; i < fragmentManager.getBackStackEntryCount(); i++){
try {
int fragmentId = fragmentManager.getBackStackEntryAt(i).getId();
fragmentManager.popBackStack(fragmentId, FragmentManager.POP_BACK_STACK_INCLUSIVE);
} catch (Exception e) {
Timber.d("Fragment Back Stack Error: %s", e.getLocalizedMessage());
}
}
}
Also if you want to prevent user to close app when no fragments at back stack, take a look at below.
#Override
public void onBackPressed() {
FragmentManager fragmentManager = getSupportFragmentManager();
if(fragmentManager.getBackStackEntryCount() > 1) {
super.onBackPressed();
} else {
// TODO: Show dialog if user wants to exit app or;
//finish();
}
}

Android fragments navigation and backstack

I have a header bar (kinda like menu) and 4 fragments (MAIN, A, B, C) from which the MAIN should be 'main/root' fragment for backstack.
Problem i have is when user via menu goes for example MAIN > A > B > C.
If i simply use backstack it will go in reverse order which i don't want.
I need back button to go back to MAIN no matter how user navigated to one of those 3.
My current code (which is wrong, it quits app when not in MAIN and current fragment is switched from other non-MAIN fragment) looks like this:
private void SwitchFragment(Fragment pFragment)
{
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.main_fl_fragmentcontainer, pFragment);
if (_CurrentFragment == _Frag_Main)
ft.addToBackStack(null);
ft.commit();
_CurrentFragment = pFragment;
}
Your stack must contains 2 fragments at maximum
Main is visible
Main is onBackstack / AorBorC is visible.
User click on back ==> Main is visible.
User click on back ==> application end
I suppose A / B / C are displayed in the same view so in this case,
When user click on your Menu, you have to check if A/ B / C is currently displayed and replace it by the one selected by the user.
private void displayFragment(Fragment pFragment) {
Fragment fr = getSupportFragmentManager()
.findFragmentById(R.id.main_fl_fragmentcontainer);
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.main_fl_fragmentcontainer, pFragment);
if (_CurrentFragment == _Frag_Main) {
ft.addToBackStack(null);
}
ft.commit();
_CurrentFragment = pFragment;
}
can override OnBackPressed method of your activity.

Categories

Resources