34

I'm using the built-in navigation drawer to run my app. I can't quite figure out how to handle the back button. When it's pressed I want it to load the very first fragment again. Fragment1.

So when the app launches you see Fragment1 launched. They can then click on Fragment 2-5 to go to other pages. Within all of these pages, I want the back button to take the user back to Fragment1. The only place the user should be able to exit the app via the back button is Fragment1.

Since it's all handled by a FragmentActivity I tried messing with the back button there. I keep getting a force close error, however:

(01-11 14:09:33.114: E/AndroidRuntime(8292): android.view.InflateException: Binary XML file line #7: Error inflating class fragment)

This is what I have so far:

I've made sure to add the fragments to the back stack like this:

fm.beginTransaction().replace(R.id.main, newFragment).addToBackStack("fragBack").commit();

Back button:

@Override
public void onBackPressed() {
    if (getSupportFragmentManager().findFragmentByTag("fragBack") != null) {
        
    }
    else {
        super.onBackPressed();
        return;
    }
    if (getSupportFragmentManager().getBackStackEntryCount() != 0) {
        Toast.makeText(getApplicationContext(), "Test", Toast.LENGTH_LONG).show();
        Fragment frag = getSupportFragmentManager().findFragmentByTag("fragBack");
        FragmentTransaction transac = getSupportFragmentManager().beginTransaction().remove(frag);
                transac.commit();
    }
    
}

Does anyone know what I need to do? Do I need to call onBackPressed in every fragment (if that's even possible) rather than the FragmentActivity that controls the drawer? In my past apps I've been OK with the back button closing the app regardless of which Fragment the user is on but the one I'm making now I want the back button to go back to Fragment1.

Would really appreciate some help, thank you.

onItemClick

@Override
        public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
            
            Fragment newFragment = new MapsPage();
            FragmentManager fm = getSupportFragmentManager();
            switch(i) {
            case 0:
                newFragment = new Fragment2();
                break;
            case 1:
                newFragment = new Fragment3();
                break;
            case 2:
                newFragment = new Fragment4();
                break;
            case 3:
                newFragment = new Fragment5();
                break;
            }
            fm.beginTransaction().add(R.id.main, newFragment).addToBackStack("fragback").commit();
            drawerLayout.closeDrawer(rl);
        }
RED_
  • 2,997
  • 4
  • 40
  • 59
  • it should work without adding any code to onBackkeypressed. the error you are getting related to layout can you post your layout as well? – vipul mittal Jan 13 '14 at 14:14
  • Really? In all of my Navigation drawers, the back button always exits the app. Regardless of what Fragment the user chose to go on. I've got custom addons to my layout but it's a standard layout, I don't think that's my problem but I will post it. – RED_ Jan 13 '14 at 14:19
  • 1
    You need to use the method `addToBackStack()` in your `FragmentTrasaction` before you commit (which you are doing). You don't need to do any more than this for the back button to take you back to the previous fragment – Dreagen Jan 13 '14 at 14:20
  • Also vipul is right, the error your getting is a problem with your layout file – Dreagen Jan 13 '14 at 14:22
  • Yes simply add Fragment1 to backstack before commit and no matter which fragment the user is on, pressing the back button will take him to Fragment1. Also make sure you don't add other Fragments to backstack – Adnan Mulla Jan 13 '14 at 14:22
  • @red_ check my ans you need to call "add" instead of replace – vipul mittal Jan 13 '14 at 14:24
  • @Dreagen I just commented out my onBackPressed and I get a force close error when pressing the back button. same Exception as in my question. – RED_ Jan 13 '14 at 14:26
  • @AdnanMulla How would I go about doing that? I have many fragments and the Fragment transaction only happens at the end. addToBackStack doesn't let me specify a class. – RED_ Jan 13 '14 at 14:30
  • possible duplicate of [How to resume Fragment from BackStack if exists](http://stackoverflow.com/questions/18305945/how-to-resume-fragment-from-backstack-if-exists) – tripleee Apr 14 '14 at 06:20

6 Answers6

37

Instead of:

fm.beginTransaction().replace(R.id.main, newFragment).addToBackStack("fragBack").commit();

Call:

fm.beginTransaction().add(R.id.main, newFragment).addToBackStack("fragBack").commit();

addToBackStack works with add.

replace function removes previous fragment and places new fragment so on your back-stack there is only one fragment all the time. So use add function to keep previous fragments on stack.

To always goto fragemnt1 from any fragment onBackPress try to do following:

getFragmentManager().popBackStack();
fm.beginTransaction().add(R.id.main, newFragment).addToBackStack("fragBack").commit();

this will remove last transaction from backstack and add new one. Try this.

vipul mittal
  • 17,343
  • 3
  • 41
  • 44
  • Thank you. That worked in getting the backstack to work. Do you know how I can have it so that it always goes back to Fragment1? So they can go to Fragment 2, then 3 but pressing the back button will take them to Fragment 1. I can post my full onItemClick if you need. – RED_ Jan 13 '14 at 14:28
  • 1
    This is just a guess - but if you don't call the `addToBackStack()` method when you change between any of the other fragments (only call it when moving away from your fragment1) then that might do it – Dreagen Jan 13 '14 at 14:31
  • That does sound logical. I've posted my onItemClick to get a better idea. – RED_ Jan 13 '14 at 14:37
  • exactly only call addToBackStack when you move from fragment1 – vipul mittal Jan 13 '14 at 14:37
  • I think what might be the issue here is that Fragment 1(MapsPage) is not part of the navigation drawer. (Or is that not an issue?). In my code Fragment 1 shows up when you launch the app, but the only way to get back to it would be the back button. It makes sense for what I am building. It's not in the navigation list. – RED_ Jan 13 '14 at 14:40
  • try to call getFragmentManager().popBackStack() every time before add – vipul mittal Jan 13 '14 at 14:47
  • It worked! Thank you so much. Thanks to @Dreagen as well. I quite like that onBackPressed was not needed at all. I put popBackStack inside each case:, just saw your edit and that would probably work too. +1 and marked as answer. – RED_ Jan 13 '14 at 14:52
  • Thank you very much man, for the cleanest approach. I was able to solve my issue: http://stackoverflow.com/questions/31578373/navigation-drawer-displaying-home-fragment-as-default/31594411#31594411 – Ritesh Kumar Gupta Jul 23 '15 at 17:36
6

Just wanted to report my findings even though this question is a little old for anyone else who may have had the same problem with the accepted answer. For me, doing the method suggested in the accepted answer, made the layers overlap, quickly making them unreadable. The code below (adapted from the accepted answer) avoids the overlaying but still adds the screen to the back stack.

fragmentManager.beginTransaction().replace(R.id.container, fragment).addToBackStack("fragBack").commit();
Kyle
  • 2,339
  • 10
  • 33
  • 67
3

In some cases you have to use replace then you cant work with addtobackstack() so you can use this code in MainActivity. In this code when you press back key you always go to first fragment (i call it HomeFragment) and when you are in HomeFragment it ask twice time to go out from application.

 private Boolean exit = false;
@Override
    public void onBackPressed() {
        if (exit) {
            super.onBackPressed();
            return;
        }

    try {
        FragmentManager fragmentManager = getSupportFragmentManager();
        Fragment fragment = fragmentManager.findFragmentByTag("HOME");
        if (fragment != null) {
            if (fragment.isVisible()) {
                this.exit = true;
                Toast.makeText(this, "Press Back again to Exit", Toast.LENGTH_SHORT).show();
            }
        }
        else {
            fragment = HomeFragment.class.newInstance();
            getFragmentManager().popBackStack();
            fragmentManager.beginTransaction().replace(R.id.flContent, fragment, "HOME").commit();
        }
    } catch (Exception e) {

    }
    new Handler().postDelayed(new Runnable() {
        @Override
        public void run() {
            exit = false;
        }
    }, 2000);
}
Muzhan
  • 31
  • 6
1
 - @Override
       public void onBackPressed() {
           DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
           int backstack = getSupportFragmentManager().getBackStackEntryCount();
           if (drawer.isDrawerOpen(GravityCompat.START)) {
               drawer.closeDrawer(GravityCompat.START);
           } else if (backstack > 0) {
              for (int i = 0; i < backstack; i++) {
                   getSupportFragmentManager().popBackStackImmediate();
            }
       } else {
           this.finish();
        }
    }
0

I have 4 fragment attached to bottomnavigation activity and i recently got into this problem and successfulyy solved as follows my activity code

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentTransaction;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import androidx.navigation.ui.NavigationUI;

import android.os.Bundle;
import android.view.MenuItem;

import com.google.android.material.bottomnavigation.BottomNavigationView;

public class BottomNavigationActivity extends AppCompatActivity
{
    NavController navController;
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_bottom_navigation);


        BottomNavigationView bottomNavigationView=findViewById(R.id.bottom_navigation);
        bottomNavigationView.setOnNavigationItemSelectedListener(navListener);

        getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container,new FragmentHome()).commit();


    }

    public boolean onSupportNavigateUp() {
        return navController.navigateUp();
    }

    private BottomNavigationView.OnNavigationItemSelectedListener navListener= new
            BottomNavigationView.OnNavigationItemSelectedListener()
            {
                @Override
                public boolean onNavigationItemSelected(@NonNull MenuItem item)
                {

                    Fragment selectedFragment = null ;


                    switch (item.getItemId())
                    {

                        case R.id.nav_home:
                            selectedFragment = new FragmentHome();

                            break;

                        case R.id.nav_search:
                            BottomNavigationActivity.this.getSupportFragmentManager().beginTransaction()
                                    .addToBackStack(null)
                                    .commit();
                            selectedFragment=new FragmentSearch();
                            break;

                        case R.id.nav_cart:
                            BottomNavigationActivity.this.getSupportFragmentManager().beginTransaction()
                                    .addToBackStack(null)
                                    .commit();
                            selectedFragment=new FragmentCart();
                            break;

                        case R.id.nav_user:
                            BottomNavigationActivity.this.getSupportFragmentManager().beginTransaction()
                                    .addToBackStack(null)
                                    .commit();
                                selectedFragment= new FragmentAccount();
                                        break;


                    }

                    getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container,
                            selectedFragment).commit();

                    return true;

                }
            };


    @Override
    public void onBackPressed()
    {
        int backStackEntryCount = getSupportFragmentManager().getBackStackEntryCount();
        if (backStackEntryCount == 0) {
            super.onBackPressed();
        } else
            {
            goHome();
        }
    }

    public void goHome()
    {
        //Following code will set the icon of the bottom navigation to active
        final BottomNavigationView mBottomNav = findViewById(R.id.bottom_navigation);
        MenuItem homeItem = mBottomNav.getMenu().getItem(0);
        mBottomNav.setSelectedItemId(homeItem.getItemId());
        getSupportFragmentManager().popBackStackImmediate();

        //To delete all entries from back stack immediately one by one.
        int backStackEntry = getSupportFragmentManager().getBackStackEntryCount();
        for (int i = 0; i < backStackEntry; i++) {
            getSupportFragmentManager().popBackStackImmediate();
        }
        //To navigate to the Home Fragment
        final FragmentHome homeFragment = new FragmentHome();
        FragmentTransaction myFragmentTransaction = getSupportFragmentManager().beginTransaction();
        myFragmentTransaction.replace(R.id.fragment_container, homeFragment, "HomeFrag Tag");
        myFragmentTransaction.commit();
    }
}
-1

I would suggest avoiding an override of onBackPressed() altogether by managing your transactions properly in the first place. This will help to avoid having to implement crazy logic down the road.

To do this first we need to set a private class variable in that will enable initialization:

private boolean popNext = false;

The following code allows us to setup the initial back function by placing it on the stack. Every time thereafter, when popNext is set to true, we pop the initial transaction and push the new one. So we are replacing the 1>X transaction with the 1>Y transaction.

The extra embedded if statements deal with selecting the initial item, since we don't want to go from 1>1. If it's our initial case we just close the drawer. The other case needs to act like the back button, but we need to remember to set it as if it is returning to the initial state!

if(popNext){
    if(i == INITIAL_POSITION){
        onBackPressed();
        mDrawerLayout.closeDrawer(mDrawerList);
        popNext = false;
        return;
    }
    getFragmentManager().popBackStackImmediate();
}
else{
    if(i == INITIAL_POSITION){
        mDrawerLayout.closeDrawer(mDrawerList);
        return;
    }
    popNext=true;
}
getFragmentManager()
.beginTransaction()
.replace(R.id.fragment_container, fragment)
.addToBackStack(null)
.commit();

Note: My code uses getFragmentManager() instead of getSupportFragmentManager() which is a native function, I believe as of Honeycomb.

Matthew Cordaro
  • 677
  • 1
  • 7
  • 26