1

Popbackstack is working fine when all the fragments in the sequence are added in the backstack but isnt working when one of the transactions is not added in the backstack.

Here is my navigation:

1.Replace fragment to load home fragment. This transaction not added to backstack.

  1. Replace fragment to load login fragment. This transaction is added to backstack.

    3.Replace fragment to load loggedin fragment. This transaction is not added to backstack.

    Now, when i press back button once nothing happens. Whereas ideally it should go to the home fragment from logged in fragment. Here is my onbackpressed method in main activity:

    @Override
    public void onBackPressed() {
        if(getSupportFragmentManager().getBackStackEntryCount()>0)
        {
            FragmentManager.BackStackEntry backStackEntry = getSupportFragmentManager().getBackStackEntryAt(getSupportFragmentManager().getBackStackEntryCount() - 1);
            String str = backStackEntry.getName();
            FragmentManager fm=getSupportFragmentManager();
            //getSupportFragmentManager().popBackStackImmediate();
            fm.popBackStack(str, FragmentManager.POP_BACK_STACK_INCLUSIVE);
        }
        else {
            super.onBackPressed();
        }
    
    
    }
    
vipul bansal
  • 57
  • 1
  • 7
  • I think it works. Just you have both fragments displayed (your LoggedInFragment AND your HomeFragment). Since the LoggedInFragment has been displayed after the HomeFragment, the HomeFragment is not visible as hidden by the LoggedInFragment. You can't expect the LoggedInFragment to disappear when you press back as the transaction is not in the back stack. – Eselfar Jul 24 '17 at 11:05
  • So to solve you issue, one solution could be to test if the current Fragment is the LoggedInFragment. If it's the case you remove the fragment from the container and then you pop the back stack to go back to home. – Eselfar Jul 24 '17 at 11:12
  • wow u are absoloutely right Thanks a ton man. – vipul bansal Jul 24 '17 at 11:13
  • but pls instead of comment, write it in answer so that we both can score a few points. – vipul bansal Jul 24 '17 at 11:14
  • My solution above is a bit messy. Another one should be to add the transaction to the bs, then when the user press back you test if it's the LoggedInFragment and you pop the bs until the homeFragment – Eselfar Jul 24 '17 at 11:14
  • I'll add an anwer – Eselfar Jul 24 '17 at 11:14
  • can u write the solution in code too? – vipul bansal Jul 24 '17 at 11:24
  • I've updated my answer with a code example. I haven't tested but should works – Eselfar Jul 24 '17 at 11:36

2 Answers2

1

popBackstack only 'pop' what is in the backstack. Since you haven't add the transaction when replacing the LoginFragment by the LoggedInFragment when you press back:

  • the LoggedInFragment remains,
  • the LogInFragment is popped
  • the HomeFragment is displayed

But because the LoggedInFragment as been added after the HomeFragment, the HomeFragment is displayed underneath it. So you can't see it as hidden by the LoggedInFragment.

One solution is to add the transaction to the back stack when you replace the LogInFragment by the LoggedInFragment. Then in onBackPressed you test if the current fragment is the LoggedInFragment. If it's the case you pop the back stack up to HomeFragment (not inclusive). Like that both LoggedInFragment and LogInFragment will be pop.

EDIT

@Override
public void onBackPressed() {
    FragmentManager manager = getSupportFragmentManager();
    Fragment fragment = manager.findFragmentById(R.id.my_fragment_container);
    // If there is something in the back stack AND the current fragment is the LoggedInFragment
    if (manager.getBackStackEntryCount() > 0
            && fragment instanceof LoggedInFragment) {            
        manager.popBackStack(HomeFragment.class.getSimpleName(), 0);
    } else {
        super.onBackPressed();
    }
}

In order to retrieve the HomeFragment by name you need to tag your transaction when you replace the current fragment by the HomeFragment. Generally I tag all transactions with the fragment's class simple name so like that I can retried any fragment:

transaction.replace(R.id.my_fragment_container, fragment, fragment.getClass().getSimpleName());

Eselfar
  • 3,759
  • 3
  • 23
  • 43
0

Eselfar's explanation of the problem is correct, but the code he provided wasn't generic enough for me.

I (hopefully) resolved this issue in a general case by the following code:

@Override
public void onBackPressed() {
    Fragment currentFragment = getCurrentFragment();

    if (mFragmentManager.getBackStackEntryCount() > 0) {

        // In a normal world, just popping back stack would be sufficient, but since android
        // is not normal, a call to popBackStack can leave the popped fragment on screen.
        // Therefore, we start with manual removal of the current fragment.
        removeCurrentFragment();

        mFragmentManager.popBackStack();
    } else {
        super.onBackPressed();
    }
}

private Fragment getCurrentFragment() {
    return mFragmentManager.findFragmentById(getContentFrameId());
}

private void removeCurrentFragment() {
    FragmentTransaction ft = mFragmentManager.beginTransaction();
    ft.remove(getCurrentFragment());
    ft.commit();

    // not sure it is needed; will keep it as a reminder to myself if there will be problems
    // mFragmentManager.executePendingTransactions();
}
Vasiliy
  • 16,221
  • 11
  • 71
  • 127
  • When is your `navigateUp()` called? To use `onBackPressed` with Fragments, your Activity needs to implement `FragmentActivity`. – Eselfar Nov 25 '17 at 22:51
  • `navigateUp()` is called when the use clicks on "up" button in toolbar. – Vasiliy Nov 26 '17 at 02:18
  • You can still call `onBackPressed()` manually in your method. If you don't want, like OP, a specific behaviour you don't even need to override `onBackPressed` itself. Just make sure your Activity extends FragmentActivity if you use Fragments – Eselfar Nov 27 '17 at 10:02
  • Up navigation and back navigation are not the same. E.g. when the user enters the app through notification on a non-landing screen, up navigation should keep him/her in the app, while back navigation should take him back to wherever he/she came from. – Vasiliy Nov 27 '17 at 10:29
  • Yeah you're right. So how your question is related to OP question as you seem to have a different issue? Basically OP tries to manage the "back" button when your case is with the "up". – Eselfar Nov 27 '17 at 13:59
  • Ah, I see what you mean now. Yes, I probably answered a bit different question than what OP asked. I removed some code from the answer such that it corresponds to "back" functionality. What do you think about it now? – Vasiliy Nov 27 '17 at 15:07
  • What are you trying to do exactly? I don't understand why you remove the Fragment and then call `popBackStack` – Eselfar Nov 27 '17 at 15:38
  • If the currently shown Fragment was added in a transaction that wasn't added to the backstack, then just popping the backstack will keep the currently shown Fragment on screen. This code resolves that by always removing the currently shown fragment in a transaction that is not added to backstack. It kind of makes `onBackPressed()` work the way I would expect - regardless of whether the last transaction was added to backstack or not, the currently shown Fragment is removed upon click on "back". – Vasiliy Nov 27 '17 at 16:18
  • Actually no. Let's say you have a Fragment A, you replace it by a Fragment B and you add the transaction in the back stack, then you replace it by a Fragment C and you don't add the transaction to the back stack. Now if you press back you expect to be on Fragment B but you are on Fragment A. Other case, you have a Fragment A and you replace it by a Fragment B without adding the transaction in the back stack, your back stack entry count will not be greater than 0, so onBackPressed will be call – Eselfar Nov 27 '17 at 16:57
  • I understand what you are trying to do but it's first very dangerous and in any case you need to refactor your code. – Eselfar Nov 27 '17 at 16:59
  • @Eselfar, I thought about what you say and I think that we have a different expectations of behavior. When I'm in Fragment B and I replace it with Fragment C without adding to the backstack, I expect that the Fragment B will not be reachable during back navigation. It is more aligned with responsibilities allocation - Fragment B controls whether it by itself is reachable during back navigation, instead of controlling the reachability of Fragment C. – Vasiliy Dec 19 '17 at 16:00
  • It depends on what exactly are your Fragments, but the user doesn't know and doesn't care about the backstack. So, pressing back he could potentially expect something different than you, the developer. – Eselfar Dec 20 '17 at 09:52