Android – Switching between Android Navigation Drawer image and Up caret when using fragments

androidandroid-actionbarandroid-fragmentsnavigation-drawer

When using the Navigation Drawer the Android devs are recommending that in the ActionBar "only those screens that are represented in the Navigation Drawer should actually have the Navigation Drawer image" and that "all other screens have the traditional up carat."

See here for details: http://youtu.be/F5COhlbpIbY

I'm using one activity to control multiple levels of fragments and can get the Navigation Drawer image to display and function at all levels.

When creating lower level fragments I can call the ActionBarDrawerToggle setDrawerIndicatorEnabled(false) to hide the Navigation Drawer image and have the Up caret displayed

LowerLevelFragment lowFrag = new LowerLevelFragment();

//disable the toggle menu and show up carat
theDrawerToggle.setDrawerIndicatorEnabled(false);
getSupportFragmentManager().beginTransaction().replace(R.id.frag_layout, 
lowFrag, "lowerFrag").addToBackStack(null).commit();

The problem I'm having is when I navigate back to the top level fragments the Up carat still shows instead of the original Navigation Drawer image. Any suggestions on how to "refresh" the ActionBar on the top level fragments to re-display the Navigation Drawer image?


Solution

Tom's suggestion worked for me. Here’s what I did:

MainActivity

This activity controls all fragments in the app.

When preparing new fragments to replace others, I set the DrawerToggle setDrawerIndicatorEnabled(false) like this:

LowerLevelFragment lowFrag = new LowerLevelFragment();

//disable the toggle menu and show up carat
theDrawerToggle.setDrawerIndicatorEnabled(false);
getSupportFragmentManager().beginTransaction().replace(R.id.frag_layout,   
lowFrag).addToBackStack(null).commit();

Next, in an override of onBackPressed, I reverted the above by setting the DrawerToggle to setDrawerIndicatorEnabled(true) like this:

@Override
public void onBackPressed() {
    super.onBackPressed();
    // turn on the Navigation Drawer image; 
    // this is called in the LowerLevelFragments
    setDrawerIndicatorEnabled(true)
}

In the LowerLevelFragments

In the fragments I modified onCreate and onOptionsItemSelected like this:

In onCreate added setHasOptionsMenu(true) to enable configuring the options menu. Also set setDisplayHomeAsUpEnabled(true) to enable the < in the actionbar:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // needed to indicate that the fragment would 
    // like to add items to the Options Menu        
    setHasOptionsMenu(true);    
    // update the actionbar to show the up carat/affordance 
    getActivity().getActionBar().setDisplayHomeAsUpEnabled(true);
}

Then in onOptionsItemSelected whenever the < is pressed it calls the onBackPressed() from the activity to move up one level in the hierarchy and display the Navigation Drawer Image:

@Override
public boolean onOptionsItemSelected(MenuItem item) {   
    // Get item selected and deal with it
    switch (item.getItemId()) {
        case android.R.id.home:
            //called when the up affordance/carat in actionbar is pressed
            getActivity().onBackPressed();
            return true;
        … 
    }

Best Solution

It's easy as 1-2-3.

If you want to achieve:

1) Drawer Indicator - when no fragments are in the Back Stack or the Drawer is opened

2) Arrow - when some Fragments are in the Back Stack

private FragmentManager.OnBackStackChangedListener
        mOnBackStackChangedListener = new FragmentManager.OnBackStackChangedListener() {
    @Override
    public void onBackStackChanged() {
        syncActionBarArrowState();
    }
};

@Override
protected void onCreate(Bundle savedInstanceState) {
    getSupportActionBar().setDisplayShowHomeEnabled(true);
    getSupportActionBar().setDisplayHomeAsUpEnabled(true);
    mDrawerToggle = new ActionBarDrawerToggle(
            this,             
            mDrawerLayout,  
            R.drawable.ic_navigation_drawer, 
            0, 
            0  
    ) {

        public void onDrawerClosed(View view) {
            syncActionBarArrowState();
        }

        public void onDrawerOpened(View drawerView) {
            mDrawerToggle.setDrawerIndicatorEnabled(true);
        }
    };

    mDrawerLayout.setDrawerListener(mDrawerToggle);
    getSupportFragmentManager().addOnBackStackChangedListener(mOnBackStackChangedListener);
}

@Override
protected void onDestroy() {
    getSupportFragmentManager().removeOnBackStackChangedListener(mOnBackStackChangedListener);
    super.onDestroy();
}

private void syncActionBarArrowState() {
    int backStackEntryCount = 
        getSupportFragmentManager().getBackStackEntryCount();
    mDrawerToggle.setDrawerIndicatorEnabled(backStackEntryCount == 0);
}

3) Both indicators to act according to their shape

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    if (mDrawerToggle.isDrawerIndicatorEnabled() && 
        mDrawerToggle.onOptionsItemSelected(item)) {
        return true;
    } else if (item.getItemId() == android.R.id.home && 
               getSupportFragmentManager().popBackStackImmediate()) {
        return true;
    } else {
        return super.onOptionsItemSelected(item);
    }
}

P.S. See Creating a Navigation Drawer on Android Developers on other tips about the 3-lines indicator behavior.

Related Question