Search code examples
androidandroid-fragmentsandroid-5.0-lollipopback-stackandroid-toolbar

Android 5.0 Toolbar fragment backstack


I've used the new Toolbar from the appcompat library to replace the action bar, and have set it up as one might expect with a navigation drawer.

My problem is that pressing back minimises the app instead of cycling through the backstack. If I manually add logic to onBackPressed() to pop the back stack, that's one solution, but messes with the navigation drawer's currently selected item.

activity_main.xml

<LinearLayout>

    <android.support.v7.widget.Toolbar/>

    <android.support.v4.widget.DrawerLayout>

        <FrameLayout
            android:id="@+id/container"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>

        <ListView android:id="@+id/navigation_drawer"/>

    </android.support.v4.widget.DrawerLayout>

</LinearLayout>

MainActivity.java

private void setupDrawerAndToolbar() {
    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);

    final DrawerLayout drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
    toggle = new ActionBarDrawerToggle(this, drawerLayout, toolbar,
        R.string.app_name, R.string.app_name);
    drawerLayout.setDrawerListener(toggle);

    drawerList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            onNavigationDrawerItemSelected(position, true);
            drawerLayout.closeDrawer(GravityCompat.START);
        }
    });
    List<NavigationDrawerItem> items = new ArrayList<>();
    items.add(...);
    drawerList.setAdapter(new NavigationDrawerAdapter(this, items));
    drawerList.setItemChecked(selectedIndex, true);
}

...

private void onNavigationDrawerItemSelected(int position, boolean addToBackStack) {
    if (position == selectedIndex) {
        return;
    }
    selectedIndex = position;

    // update the main content by replacing fragments
    FragmentManager manager = getFragmentManager();

    Fragment fragment;

    switch (position) {
        case ...:
            fragment = new Fragment();
            break;
        default:
            throw new IllegalArgumentException("invalid position");
    }

    FragmentTransaction transaction =
        manager.beginTransaction()
               .replace(R.id.container, fragment);

    if (addToBackStack) {
        transaction.addToBackStack(fragment.getClass().getName());
    }

    transaction.commit();
}

Solution

  • Came up with a workaround:

    private void onNavigationDrawerItemSelected(int position, boolean addToBackStack) {
        if (position == selectedIndex) {
            return;
        }
    
        // update the main content by replacing fragments
    
        Fragment fragment;
    
        switch (position) {
            case ...:
                fragment = new Fragment();
                break;
            default:
                throw new IllegalArgumentException("invalid position");
        }
    
        FragmentTransaction transaction =
            getFragmentManager().beginTransaction()
                                .replace(R.id.container, fragment, fragment.getClass().getName());
    
        if (addToBackStack) {
            transaction.addToBackStack(String.valueOf(selectedIndex));
        }
    
        transaction.commit();
        selectedIndex = position;
    }
    
    ...
    
    @Override
    public void onBackPressed() {
        int count = getFragmentManager().getBackStackEntryCount();
        if (count > 0) {
            int index = Integer.parseInt(
                getFragmentManager().getBackStackEntryAt(count - 1).getName());
            getFragmentManager().popBackStack();
            selectedIndex = index;
            return;
        }
        super.onBackPressed();
    }