I have a NavigationDrawer for my main activity, in one instance I show a fragment and the "Hamburger menu" changes into an arrow.
I disabled the ability to open the drawer via swipe when that fragment is shown by using this
mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
and it works fine but now the problem is the user can still open the drawer by clicking the home button "Arrow". I want the arrow to act as the back button and not open the drawer so how can I stop the drawer from opening?
I guess you need to use setNavigationOnClickListener() on toolbar to override drawer behavior.
Like -
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// logic to decide if drawer open/close, or pop fragment etc
}
});
This does prevents drawer from opening by clicking on icon. But it does open by swiping. But that you already taken care of. So this should work.
Note This works only after you set ActionBarDrawerToggle by calling
mDrawerToggle = new ActionBarDrawerToggle(getActivity(), mDrawerLayout, toolbar, R.string.openDrawer, R.string.closeDrawer){ ...
Put this on your Activity:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getId() == android.R.id.home){
doSomething(); //can be a finish()
}
}
Or, from this answer
mDrawerToggle.setToolbarNavigationClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
popStackIfNeeded();
mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);
mActionBar.setDisplayHomeAsUpEnabled(false);
mDrawerToggle.setDrawerIndicatorEnabled(true);
}
});
You could try overriding onOptionsItemSelected check for the id android.R.id.home and call finish() to go back to your previous activity.
try to override on options item select.
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home :
// do what you want
return true;
}
return true;
}
Related
I'm scratching my head with this one now.... I have an ActionBarActivity that loads an initial Fragment - the original menu is inflated within the activity. Now, I have a navigation bar that, when an item is selected, loads a different fragment and adds this to the backstack.
When I do this, there are a couple of things I want to set:
Set the home as up indicator
Invalidate the options menu from the main activity
Set has options to true for the Fragment
Ensure that the up indicator correctly navigates back to the original Fragment
Something rather strange is going on - the up indicator appears once only and does not behave as the back button and although I've invalidated and inflated a new menu, the new menu gets appended to the original Activity menu.
EDIT: Ok I've resolved the appending issue - forgot to add menu.clear() in the onCreateOptionsMenu method.
My navigation drawer layout has onClick methods to all menu items which would trigger the load of another Fragment:
public void navItemClick(View view) {
switch (view.getId()) {
case R.id.ripSMS:
mNavigationDrawer.toggleHome(false);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
FragmentTransaction mTrans = getSupportFragmentManager().beginTransaction();
mTrans.replace(R.id.voiceover_frame_layout,new MessageFragment(),"main_ui")
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE).addToBackStack("msg").commit();
break;
case R.id.ripEmail:
break;
case R.id.ripSettings:
break;
}
mNavigationDrawer.closeDrawer();
}
toggleHome:
public void toggleHome(boolean show) {
mDrawerToggle.setDrawerIndicatorEnabled(show);
if (!show) {
mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
} else {
mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);
}
}
Once the item is triggered the onCreate contains the invalidate and the hasOptions code:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getActivity().invalidateOptionsMenu();
setHasOptionsMenu(true);
}
The onCreateOptionsMenu then inflates another menu layout (contains a single item called settings).
As mentioned, this only partially works once - the first time I use the item to load the Fragment, I get the back icon but it's also not working (this is set within onOptionsItemSelected to check for the home item press - it does nothing). When I press the back button it takes me back to the correct place. If I navigate back however, the back arrow now longer shows even though the code runs through onCreate!
Ok so I managed to solve this after some trial and error. Two changes made:
Implement addOnBackStackChangedListener
ActionBarDrawerToggle's setToolbarNavigationClickListener needed to be set
As I only have one activity (everything else is Fragment classes) I added the backstack listener to the Parent Activity's onCreate method:
getSupportFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
#Override
public void onBackStackChanged() {
if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
} else {
getSupportActionBar().setDisplayHomeAsUpEnabled(false);
}
}
});
This resolved the disappearing back arrow when going back to the fragment. Finally added the listener to my NavigationDrawer's setup class:
mDrawerToggle.setToolbarNavigationClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
getActivity().onBackPressed();
}
});
I suppose the only questions I have is everything pointed towards using the onOptionsItemSelected method with android.R.id.home but this never worked for me. It might be the way I've implemented things of course but if someone sees anything obvious as to why please do let me know!
These steps helps you to show back button in toolbar when a fragment is loaded. And to pop out when its clicked.
Set setNavigationOnClickListener to toolbar in you activity.
final DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
toggle = new ActionBarDrawerToggle(
this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
drawer.setDrawerListener(toggle);
toggle.syncState();
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(getSupportFragmentManager().getBackStackEntryCount() > 0){
getSupportFragmentManager().popBackStack();
}else {
drawer.openDrawer(GravityCompat.START);
}
}
});
Implement FragmentManager.OnBackStackChangedListener in you Activity. And register it with SupportFragmentManager in OnCreate()
getSupportFragmentManager().addOnBackStackChangedListener(this);
OnBackStackChangedListener Implementation method:
#Override
public void onBackStackChanged() {
if(getSupportFragmentManager().getBackStackEntryCount() > 0){
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}else {
getSupportActionBar().setDisplayHomeAsUpEnabled(false);
toggle.syncState();
}
}
For me the above answer was not enough, but i've used #Hamz4h_ and added some more after digging into the ActionBarDrawerToggle class. I'm just calling this method of mine from the activity's onCreate:
private void initNavigationElements() {
final ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
this, mBinding.drawerLayout, mBinding.appBarMain.toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
mBinding.drawerLayout.addDrawerListener(toggle);
// Tricking the toggle by giving it its own arrow as a custom indicator.
// It will use it when setDrawerIndicatorEnabled is called with false
toggle.setHomeAsUpIndicator(toggle.getDrawerArrowDrawable());
toggle.syncState();
getSupportFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
#Override
public void onBackStackChanged() {
DrawerArrowDrawable drawerArrowDrawable = toggle.getDrawerArrowDrawable();
if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
// 1 - Display as arrow
drawerArrowDrawable.setProgress(1);
toggle.setDrawerIndicatorEnabled(false);
} else {
// 2 - Display as arrow menu
drawerArrowDrawable.setProgress(0);
toggle.setDrawerIndicatorEnabled(true);
}
}
});
toggle.setToolbarNavigationClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// This is called only when setDrawerIndicatorEnabled is set as false, meaning we are not at the "root" fragment.
getSupportFragmentManager().popBackStackImmediate();
}
});
}
Hope this will help someone :)
I've been searching for a while on how to change between the drawer open/close icon (going from a hamburger to the arrow) to a simple back arrow. My application at the moment only has one Activity which switches between several fragments. At one point, I want to transition between one of the main fragments (ie, one of the fragments in the drawer) to a fragment that hierarchically is under the previous fragment (ie, an "Add New " fragment). In this new fragment, I want to have the Toolbar to show the back button instead of the drawer button.
I've been looking around and trying different solutions for quite a while. Here are the most notable:
Change drawer icon back to back arrow - I successfully removed the drawer icon, but in place there's.... nothing. No up caret, no back button, no icon. I suspect this is because my Activity has no parent, but other than a cheap work around (create another Activity that acts as a parent which launches the main Activity), I'm at a lost of what to do.
Switching between Android Navigation Drawer image and Up caret when using fragments - Similar to the above, yet has far more detail. Ultimately, the icon still doesn't turn into a back button.
Android lollipop toolbar switch between open/close drawer and back button - I find this hard to follow, but ultimately the drawer icon can be tapped and does nothing (although I believe I know how to make it act as a back press). However, the icon doesn't change.
At the moment, I'm thinking of a long, arduous method of creating a custom icon that I hide and show (and hide/show the native drawer icon). However, is there a better way to switch between the drawer and back buttons?
As a side yet related question, I've been looking at the Material Design docs, and a few examples have an X in the top left corner. How different is that to implement than implementing the drawer vs back/up buttons?
Thanks~
Edit:
I can figure out how to replace the icon, but how would I get the click event?
So far, this was my best lead:
Cannot catch toolbar home button click event
What I've tried now:
Disabled the DrawerToggle when necessary (ie, mDrawerToggle.setDrawerIndicatorEnabled(useDrawer);)
Added logs in onOptionsItemSelected in my NavigationDrawerFragment, my Activity, as well as the DialogFragment I'm currently testing which run if item.getItemId() == android.R.id.home is true. None of these log statements go off
For better context, I now have a full screen fragment which adds a "Save" button to the menu and changes the drawer icon to an "X". The fragment can get the save menu event, yet not even the Activity and Drawer can get when the X is tapped.
Edit2:
As requested, here is some code. Note that this is all from this Github repo, which I'm actively working on (note that I have a few useless functions here or there from rapid testing).
ActivityMain:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Add the toolbar
mToolbar = (Toolbar) findViewById(R.id.toolbar);
if (mToolbar != null) {
setSupportActionBar(mToolbar);
}
// Initialize the drawer
mNavigationDrawerFragment = (NavigationDrawerFragment)
getSupportFragmentManager().findFragmentById(R.id.navigation_drawer);
// Set up the drawer
mNavigationDrawerFragment.setUp(
R.id.navigation_drawer,
(DrawerLayout) findViewById(R.id.drawer_layout),
mToolbar);
// TODO: Check if this helps to catch the main toolbar button click
getSupportActionBar().setDisplayShowHomeEnabled(true);
// Get the titles for the Toolbar
mTitles = getResources().getStringArray(R.array.drawer_items);
mDrawerPosition = -1;
if (savedInstanceState == null) {
// If there was no saved position, then the default, starting position should be used
forceChangeItemSelected(0);
}
else {
// Otherwise, get the saved position from the bundle
int position = savedInstanceState.getInt(KEY_DRAWERPOS);
mNavigationDrawerFragment.setSelectedItem(position);
// Title needs to be re-set
getSupportActionBar().setTitle(mTitles[position]);
}
// If I include the below bit, then the DrawerToggle doesn't function
// I don't know how to switch it back and forth
mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.d(LOG_TAG, "Navigation was clicked");
}
});
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
Log.d(LOG_TAG, "Activity responding to menu click...");
if(item.getItemId() == android.R.id.home) Log.d(LOG_TAG, "Activity got it....");
// If the fragment is supposed to handle things, then let it
if(mIsFragmentHandlingMenus) return false;
int id = item.getItemId();
if(id == R.id.save) {
// This isn't implemented! If chosen, then there's a bug!
Log.e(LOG_TAG, "onOptionsItemSelected: Save was selected!");
}
return super.onOptionsItemSelected(item);
}
#Override
public void fragmentHandlingMenus(boolean isFragmentHandlingMenus) {
// Simply store the setting
mIsFragmentHandlingMenus = isFragmentHandlingMenus;
// Toggle the drawer as necessary
mNavigationDrawerFragment.toggleDrawerUse(!isFragmentHandlingMenus);
}
NavigationDrawerFragment:
public void toggleDrawerUse(boolean useDrawer) {
// Enable/Disable the icon being used by the drawer
mDrawerToggle.setDrawerIndicatorEnabled(useDrawer);
// TODO: Enable/Disable the drawer even being able to open/close
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
Log.d(LOGTAG, "Drawer responding to menu click...");
if(item.getItemId() == android.R.id.home) Log.d(LOGTAG, "Drawer got it....");
if (mDrawerToggle.onOptionsItemSelected(item)) {
return true;
}
return super.onOptionsItemSelected(item);
}
GoalAdderFragment:
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
// Allow this fragment to handle toolbar menu items
setHasOptionsMenu(true);
// Set up the toolbar
((ActionBarActivity) getActivity()).getSupportActionBar().setDisplayHomeAsUpEnabled(true);
((ActionBarActivity) getActivity()).getSupportActionBar().setHomeAsUpIndicator(android.R.drawable.ic_menu_close_clear_cancel);
((ActionBarActivity) getActivity()).getSupportActionBar().setTitle(getResources().getString(R.string.title_addgoal));
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// Cache the Activity as the frag handler if necessary
if(mFragHandler == null)
mFragHandler = (TransactionHandler.FragmentTransactionHandler) getActivity();
// Tell the Activity to let fragments handle the menu events
mFragHandler.fragmentHandlingMenus(true);
}
#Override
public void onDetach() {
super.onDetach();
// Tell the Activity that it can now handle menu events once again
mFragHandler.fragmentHandlingMenus(false);
}
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.save_menu, menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
Log.d(LOGTAG, "Item id: " + item.getItemId() + " | Save id: " + R.id.save);
Toast.makeText(getActivity(), "Fragment activated!", Toast.LENGTH_SHORT).show();
switch (item.getItemId()) {
case R.id.save:
return true;
case android.R.id.home:
return true;
default:
break;
}
return false;
}
Solution:
This is the ultimate solution I ended up on, with the help of natario's answer below:
NavigationDrawerFragment:
private View.OnClickListener mOriginalListener;
public void setUp(int fragmentId, DrawerLayout drawerLayout, Toolbar toolbar) {
/* Rest of setting up code */
// Save the default listener after setting everything else up
mOriginalListener = mDrawerToggle.getToolbarNavigationClickListener();
}
// Tells the toolbar+drawer to switch to the up button or switch back to the normal drawer
public void toggleDrawerUse(boolean useDrawer) {
// Enable/Disable the icon being used by the drawer
mDrawerToggle.setDrawerIndicatorEnabled(useDrawer);
// Switch between the listeners as necessary
if(useDrawer)
mDrawerToggle.setToolbarNavigationClickListener(mOriginalListener);
else
mDrawerToggle.setToolbarNavigationClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(getActivity(), "Custom listener", Toast.LENGTH_SHORT).show();
}
});
}
Put this code into onCreate() of your Activity. Works well for me. Even using compileSdk 23 and higher.
drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
final Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
if(toolbar != null) {
toggle = new ActionBarDrawerToggle(
this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
toggle.syncState();
drawer.setDrawerListener(toggle);
getSupportFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
#Override
public void onBackStackChanged() {
if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true); // show back button
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
onBackPressed();
}
});
} else {
//show hamburger
getSupportActionBar().setDisplayHomeAsUpEnabled(false);
toggle.syncState();
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
drawer.openDrawer(GravityCompat.START);
}
});
}
}
});
It should work even for latest API 24.
In your activity onCreate() do this:
final Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
final DrawerLayout drawer = (DrawerLayout) view.findViewById(R.id.drawer_layout);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
final ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(this, drawer, toolbar,
R.string.navigation_drawer_open, R.string.navigation_drawer_close);
drawer.addDrawerListener(toggle);
toggle.syncState();
final View.OnClickListener originalToolbarListener = toggle.getToolbarNavigationClickListener();
getSupportFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
#Override
public void onBackStackChanged() {
if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
toggle.setDrawerIndicatorEnabled(false);
toggle.setToolbarNavigationClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
getSupportFragmentManager().popBackStack();
}
});
} else {
toggle.setDrawerIndicatorEnabled(true);
toggle.setToolbarNavigationClickListener(originalToolbarListener);
}
}
});
That's probably not what you would like to hear, but even from a conceptual point of view I would go for a new activity rather than a fragment.
Your main activity is strictly linked to the drawer, so loading a new fragment without any access to the drawer makes no sense to me (but feel free wait for other answers if you think so). A new activity would solve both problems, since it would have no drawer and could be a child of the main one.
Your side question looks spot on also. A "Add New" activity could nicely fit into the "full-screen dialog" visual pattern from the guidelines. See:
http://www.google.com/design/spec/components/dialogs.html#dialogs-full-screen-dialogs
This pattern has a "save", positive button on top-right, and a X. Conceptually, the X button is to cancel/abort a process, rather than navigating up some backstack. It means you are dismissing something without letting any action happen. This fits well for what you want to do.
From a design point of view, it's easily made by a new Activity, that can stay on top of others. Also, if the point of fragments is basically being able to represent two or more at once in tablets and bigger screen - again - I wouldn't be so happy with an old fragment on my left and an "Add New" fragment on the right.
Rather - on tablets - I would go for a floating dialog, as suggested by the guidelines.
http://www.google.com/design/spec/components/dialogs.html#dialogs-confirmation-dialogs
So full-screen activity with a X button for phones, and floating dialog (with buttons at the bottom) for tablets. This, to me, is the most guidelines-coherent approach.
I recommend reading the whole link. On the difference between <- and X,
The X differs from an Up arrow, which is used when the view’s state is constantly being saved or when apps have draft or autosave capabilities. For example, an Up arrow is used in Settings because all changes are committed immediately.
And also
Touching the X in this Settings example will discard all changes. Changes will be saved only upon touching Save.
The answer from #matusalem works great. I just had one bit to add to it - be careful because the drawer can also be opened by swiping in from the left side of the screen. For some, this may be desired, but for me I was disabling the drawer because it didn't make sense in any fragment but my main fragment. The swipe is easily disabled here -
Navigation drawer - disable swipe
This probably belongs in a comment to the answer, but I don't have enough reputation. My apologies.
I had same problem with switching between hamburger menu and back arrow inside same activity when changing fragments. Here is my working solution, hope it helps to someone.
Listener inside your activity:
private View.OnClickListener toolbarMenuListener = new View.OnClickListener() {
#Override
public void onClick(View v) {
//will be called only if toggle.setDrawerIndicatorEnabled(false); !
Log.v(tag,"toggle onClick:"+v.getId()+" android.R.id.home:"+android.R.id.home);
onBackPressed();
}
};
Code onCreate() something like:
...
...
setSupportActionBar(toolbar);
toggle = new ActionBarDrawerToggle(this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
drawer.addDrawerListener(toggle);
toggle.syncState();
//set listener so you know when back on arrow is pressed
toggle.setToolbarNavigationClickListener(toolbarMenuListener);
...
...
Part you are interested in with comments (Class returned is some of mine class, can set to be void):
/**
* Method to set up action bar drawer.
* #param enableBackDrawerIcon set true if want to show drawer back arrow,
* false to show hamburger menu.
* #param title shown next to drawer icon
*/
public BaseMenusActivity drawerSetupToggle(boolean enableBackDrawerIcon, String title) {
//NOTE: order of methods call is important!
// If you change order order of setDrawerIndicatorEnabled and setDisplayHomeAsUpEnabled
// method calls it won't work, weird bugs will happen (like no icon at all)
if(enableBackDrawerIcon){
Log.v(tag,"show drawer back icon");
//hides hamburger menu and enables View.OnClickListener to be called
toggle.setDrawerIndicatorEnabled(false);
//show back arrow
if(getSupportActionBar()!=null)
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
} else {
Log.v(tag,"show hamburger menu");
//hide back arrow
if(getSupportActionBar()!=null)
getSupportActionBar().setDisplayHomeAsUpEnabled(false);
//shows hamburger menu and prevents View.OnClickListener to be called
toggle.setDrawerIndicatorEnabled(true);
}
setTitle(title);
return this;
}
NOTE: order of called methods is important! Would be better if could just write it in 2 lines like this but WON'T WORK (at least for me):
toggle.setDrawerIndicatorEnabled(!enableBackDrawerIcon);
getSupportActionBar().setDisplayHomeAsUpEnabled(enableBackDrawerIcon);
If you are interested why order of method calls mess things up, look into implementation of those methods.
//This if block makes the menu back button to respond to clicks
//The onOptionsItemSelected fun for whatever reason was not capturing back menu clicks
if (toolbar != null) {
/* toggle = ActionBarDrawerToggle(
this, drawer_layout, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close)
toggle.syncState()
drawer_layout.setDrawerListener(toggle)*/
supportFragmentManager.addOnBackStackChangedListener(object : FragmentManager.OnBackStackChangedListener {
override fun onBackStackChanged() {
if (supportFragmentManager.backStackEntryCount > 0) {
supportActionBar?.setDisplayHomeAsUpEnabled(true) // show back button
toolbar.setNavigationOnClickListener(object : View.OnClickListener {
override fun onClick(v: View) {
onBackPressed()
}
})
} else {
//show hamburger
supportActionBar?.setDisplayHomeAsUpEnabled(false)
toggle.syncState()
toolbar.setNavigationOnClickListener(object : View.OnClickListener {
override fun onClick(v: View) {
drawer_layout.openDrawer(GravityCompat.START)
}
})
}
}
})
}
You need to comment out "toggle = ActionBarDrawerToggle(
this, drawer_layout, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close)
toggle.syncState()
drawer_layout.setDrawerListener(toggle)" (4-7 lines) if you are using the auto generated Navigation layout in Android Studio, else the behavior of the back menu button will be erratic. That is what i did and it worked perfectly for me. Hope this helps someone
I'm swapping out the action bar for the tool bar, and I nearly have every piece of the puzzle in place. My issue is specifically if I navigate 'up' and restore the navigation drawer, the drawer toggle button no longer works. I figured out if I set the drawer mode to unlocked I have the ability to swipe to open the drawer, but can't click to open the drawer.
So I load fragment A, drawer behaviour is fine, go down to fragment B and apply the up icon, hit up to go back to A, and the drawer won't open with a click any more.
Entering Fragment B:
Toolbar t = mHostingActivity.getToolbar();
mHostingActivity.getDrawerToggle().setDrawerIndicatorEnabled(false);
mHostingActivity.getSupportActionBar().setDisplayHomeAsUpEnabled(true);
t.setNavigationIcon(mHostingActivity.getV7DrawerToggleDelegate().getThemeUpIndicator());
t.setNavigationOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
popBackStackToTop(mHostingActivity);
}
});
/**
* Pop the back stack and hide the Up caret all the way to the top level of the {#link com.loylap.activities.MainActivity}
*
* #param activity our hosting activity
*/
public static void popBackStackToTop(MainActivity activity) {
if (activity != null) {
FragmentManager fm = activity.getSupportFragmentManager();
fm.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
activity.getDrawerLayout().setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);
activity.getDrawerToggle().setDrawerIndicatorEnabled(true);
}
}
The navigation drawer is set up just like the sample, maybe the old way of setting up the options is the issue? For example, I still have this in my activity:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
if (mDrawerToggle.onOptionsItemSelected(item)) {
return true;
}
return super.onOptionsItemSelected(item);
}
EDIT:
Okay so I've narrowed down the issue to the setNavigationOnClickListener(). If I don't set this (and go up via back button) - the drawer behaves correctly. So now the question is how do I correctly allow the user to go 'up', and restore the click listener after to we do go up?
So I've figured out I was creating the wrong click listener. Instead of setNavigationOnClickListener(), I need setToolbarNavigationClickListener() :)
A subtle but important change, now the tool bar is behaving in partnership with the v7 ActionBarDrawerToggle
/**
* Create the Up caret for a lower level fragment {#link com.loylap.activities.MainActivity}
*
* #param activity our hosting activity
*/
public static void createUpButton(final MainActivity activity)
{
ActionBarDrawerToggle toggle = activity.getDrawerToggle();
//Disables onClick toggle listener (onClick)
toggle.setDrawerIndicatorEnabled(false);
toggle.setToolbarNavigationClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
popBackStackToTop(activity);
}
});
Toolbar t = activity.getToolbar();
t.setNavigationIcon(activity.getV7DrawerToggleDelegate().getThemeUpIndicator());
activity.getDrawerLayout().setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
}
In my case it was a matter of order, I needed to first set the toolbar and than set the on click listener. in this order:
//works
setSupportActionBar(myToolbar);
myToolbar.setNavigationOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
openDrawer(view);
}
});
rather than this:
//doesn't work
myToolbar.setNavigationOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
openDrawer(view);
}
});
setSupportActionBar(myToolbar);
Inspired by the solution of Daniel Wilson but you only have to do it once and it is all set.
In my NavigationDrawer's setUp() (or you can do it anywhere you are initialising your ActionBarDrawerToggle instance), I write this code:
mDrawerToggle.setToolbarNavigationClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(!mDrawerToggle.isDrawerIndicatorEnabled()) {
getActivity().onBackPressed();
}
}
});
Now every time android.id.home is pressed and hamburger sign is not shown, the parent activity's onBackPressed() is called.
I think you can't use:
t.setNavigationIcon(mHostingActivity.getV7DrawerToggleDelegate().getThemeUpIndicator());
t.setNavigationOnClickListener(new View.OnClickListener() ...
because it will break your normal navigation drawer behaviour.
Instead try something like this in onCreateOptionsMenu(Menu menu, MenuInflater inflater):
mHostingActivity.getDrawerToggle().setDrawerIndicatorEnabled(false);
mHostingActivity.getDrawerToggle().setHomeAsUpIndicator(mHostingActivity.getV7DrawerToggleDelegate().getThemeUpIndicator());
and then in onOptionsItemSelected
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
popBackStackToTop(mHostingActivity);
return true;
default:
break;
}
return false;
}
PS: don't forget to use setHasOptionsMenu(true); in your fragment onCreateView.
To animate we can use.
ValueAnimator drawerAnimator = ValueAnimator.ofFloat(Constants.HAMBURGER, Constants.BACK);
drawerAnimator.addUpdateListener(drawerAnimationUpdateListener);
drawerAnimator.setDuration(Constants.DRAWER_ANIMATION_DURATION);
drawerAnimator.setInterpolator(new LinearInterpolator());
pass action 0 for HAMBURGER icon and 1 for BACK.
public void updateNavigationDrawer(int action) {
drawerArrowDrawable = actionBarDrawerToggle.getDrawerArrowDrawable();
if (action == Constants.BACK) {
actionBarDrawerToggle.setDrawerIndicatorEnabled(false);
actionBarDrawerToggle.setHomeAsUpIndicator(drawerArrowDrawable);
actionBarDrawerToggle.setToolbarNavigationClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//onBackPress();
}
});
drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED););
} else {
actionBarDrawerToggle.setDrawerIndicatorEnabled(true);
drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);
}
if (drawerArrowDrawable.getProgress() != action) {
if (action == Constants.BACK) {
drawerAnimator.start();
} else {
drawerAnimator.reverse();
}
}
}
I have made an app with one activity which uses a navigation drawer to open a number of different fragments. I have the actionbar drawertoggle, but it is not very visible.
If I place a button in the onCreateView in my main fragment(the fragment that appears when my app first starts up), how can I get it to open the navigation drawer controlled by my activity?
This seems to work. The answer is much simpler than I thought it would be.
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View fragView = inflater.inflate(R.layout.mainmenu, container, false);
button1 = (Button) fragView.findViewById(R.id.button1);
mDrawerLayout = (DrawerLayout)getActivity().findViewById(R.id.drawer_layout);
mDrawerList = (ListView)getActivity().findViewById(R.id.left_drawer);
button1.setOnClickListener(this);
return fragView;
}
#Override
public void onClick(View v) {
mDrawerLayout.openDrawer(mDrawerList);
}
Thank you for your answers.
if you need open the slide:
mDrawerLayout.openDrawer(Gravity.LEFT); //Edit Gravity.START need API 14
if you need close the slide
mDrawerLayout.closeDrawer(Gravity.LEFT); //Edit Gravity.START need API 14
EXAMPLE
my mDrawerLayout is instanced here:
mDrawerLayout = (DrawerLayout)findViewById(R.id.my_drawer_layout);
my slide state:
mSlideState=false;
if you need to know the slide menu state (closed, opened). Use this code:
mDrawerLayout.setDrawerListener(new ActionBarDrawerToggle(this,
mDrawerLayout,
R.drawable.ic_menu_slide,
0,
0){
#Override
public void onDrawerClosed(View drawerView) {
super.onDrawerClosed(drawerView);
mSlideState=false;//is Closed
}
#Override
public void onDrawerOpened(View drawerView) {
super.onDrawerOpened(drawerView);
mSlideState=true;//is Opened
}});
finally. You can use your click event like this:
public void clickEventSlide(){
if(mSlideState){
mDrawerLayout.closeDrawer(Gravity.END);
}else{
mDrawerLayout.openDrawer(Gravity.END);
}}
In my case, my slide menu is at the right (Gravity.END), but if you need it on the left, try with Gravity.START
You Should Use isDrawerOpen()
The piece of code below automatically closes or opens the navigation drawer based on the drawer's current state (Opened or Closed)
Button hamMenu = findViewById(R.id.ham_menu);
hamMenu.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
DrawerLayout navDrawer = findViewById(R.id.drawer_layout);
// If the navigation drawer is not open then open it, if its already open then close it.
if(!navDrawer.isDrawerOpen(Gravity.START)) navDrawer.openDrawer(Gravity.START);
else navDrawer.closeDrawer(Gravity.END);
}
});
if you are using from default navigation activity in android you just have to
add this code in click listener of button --->
mDrawerLayout.openDrawer(Gravity.START);
for closing you do not have to do something.
Use these lines to open and close the drawer on a certain event:
Code snippet for opening drawer:
drawerLayout.openDrawer(Gravity.START);
Code snippet for closing drawer:
drawerLayout.closeDrawer(Gravity.LEFT);
→ openDrawer(gravity_of_navigation_view_to_be_shown)
in openDrawer("gravity"), in "gravity" section, you have to input the gravity of the Navigation View like given above:
Gravity.LEFT
Gravity.RIGHT
Gravity.START
Gravity.END
I think thats the best answer.
To apply the toolbar as the app bar, first make sure your activity extends from AppCompatActivity. Then call setSupportActionBar() and pass the Toolbar object from your layout:
toolbar=(Toolbar) findViewById(R.id.toolbar_main);
setSupportActionBar(toolbar);
ActionBar actionbar = getSupportActionBar();
actionbar.setDisplayHomeAsUpEnabled(true);
actionbar.setHomeAsUpIndicator(R.drawable.ic_menu_black_24dp);
drawerLayout=(DrawerLayout)findViewById(R.id.drawer_layout);
ActionBarDrawerToggle actionBarDrawerToggle=new ActionBarDrawerToggle(this,drawerLayout,R.string.navigation_drawer_open,
R.string.navigation_drawer_close);
drawerLayout.addDrawerListener(actionBarDrawerToggle);
actionBarDrawerToggle.syncState();
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
drawerLayout.openDrawer(GravityCompat.START);
return true;
}
return super.onOptionsItemSelected(item);
}
#Override
public void onBackPressed() {
if (drawerLayout.isDrawerOpen(GravityCompat.START)){
drawerLayout.closeDrawer(GravityCompat.START);
}
else{
super.onBackPressed();
}
}
}
The simplest way in my opinion
#Override
public void onBackPressed() {
if(mDrawerLayout.isDrawerOpen(findViewById(R.id.navigationViewId))){
mDrawerLayout.closeDrawer(Gravity.LEFT);
}else
super.onBackPressed();
}
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;
…
}
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.
You have written that, to implement lower-level fragments, you are replacing the existing fragment, as opposed to implementing the lower-level fragment in a new activity.
I would think that you would then have to implement the back functionality manually: when the user pressed back you have code that pops the stack (e.g. in Activity::onBackPressed override). So, wherever you do that, you can reverse the setDrawerIndicatorEnabled.
I've used next thing:
getSupportFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
#Override
public void onBackStackChanged() {
if(getSupportFragmentManager().getBackStackEntryCount() > 0){
mDrawerToggle.setDrawerIndicatorEnabled(false);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
else {
getSupportActionBar().setDisplayHomeAsUpEnabled(false);
mDrawerToggle.setDrawerIndicatorEnabled(true);
}
}
});
If your up action bar button doesn't work, don't forget to add the listener :
// Navigation back icon listener
mDrawerToggle.setToolbarNavigationClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
onBackPressed();
}
});
I've got some trouble implementing a drawer navigation with a home button, everything worked except the action buton.
Try handling the Home item selection in the MainActivity depending on the state of the DrawerToggle. This way you don't have to add same code to every fragment.
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Only handle with DrawerToggle if the drawer indicator is enabled.
if (mDrawerToggle.isDrawerIndicatorEnabled() &&
mDrawerToggle.onOptionsItemSelected(item)) {
return true;
}
// Handle action buttons
switch (item.getItemId()) {
// Handle home button in non-drawer mode
case android.R.id.home:
onBackPressed();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
FOLLOW UP
The solution given by #dzeikei is neat, but it can be extended, when using fragments, to automatically handle setting back the drawer indicator when the backstack is empty.
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Only handle with DrawerToggle if the drawer indicator is enabled.
if (mDrawerToggle.isDrawerIndicatorEnabled() &&
mDrawerToggle.onOptionsItemSelected(item)) {
return true;
}
// Handle action buttons
switch (item.getItemId()) {
// Handle home button in non-drawer mode
case android.R.id.home:
// Use getSupportFragmentManager() to support older devices
FragmentManager fragmentManager = getFragmentManager();
fragmentManager.popBackStack();
// Make sure transactions are finished before reading backstack count
fragmentManager.executePendingTransactions();
if (fragmentManager.getBackStackEntryCount() < 1){
mDrawerToggle.setDrawerIndicatorEnabled(true);
}
return true;
default:
return super.onOptionsItemSelected(item);
}
}
EDIT
For the question of #JJD.
The fragments are held/managed in an activity. The above code is written once in that activity, but only handle the up caret for the onOptionsItemSelected.
In one of my apps I also needed to handle the behavior of the up caret when the back button was pressed. This can be handle by overriding onBackPressed.
#Override
public void onBackPressed() {
// Use getSupportFragmentManager() to support older devices
FragmentManager fragmentManager = getFragmentManager();
fragmentManager.executePendingTransactions();
if (fragmentManager.getBackStackEntryCount() < 1){
super.onBackPressed();
} else {
fragmentManager.executePendingTransactions();
fragmentManager.popBackStack();
fragmentManager.executePendingTransactions();
if (fragmentManager.getBackStackEntryCount() < 1){
mDrawerToggle.setDrawerIndicatorEnabled(true);
}
}
};
Note the code duplication between onOptionsItemSelected and onBackPressed which can be avoided by creating a method and calling that method in both places.
Also note I add two more times executePendingTransactions which in my case was required or else I had sometimes strange behaviors of the up caret.
I created an interface for the hosting activity to update the view state of the hamburger menu. For top level fragments I set the toggle to true and for fragments for which I want to display the up < arrow I set the toggle to false.
public class SomeFragment extends Fragment {
public interface OnFragmentInteractionListener {
public void showDrawerToggle(boolean showDrawerToggle);
}
private OnFragmentInteractionListener mListener;
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
this.mListener = (OnFragmentInteractionListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implement OnFragmentInteractionListener");
}
}
#Override
public void onResume() {
super.onResume();
mListener.showDrawerToggle(false);
}
}
Then in my Activity ...
public class MainActivity extends Activity implements SomeFragment.OnFragmentInteractionListener {
private ActionBarDrawerToggle mDrawerToggle;
public void showDrawerToggle(boolean showDrawerIndicator) {
mDrawerToggle.setDrawerIndicatorEnabled(showDrawerIndicator);
}
}
This answer was working but there was a little problem with it.
The getSupportActionBar().setDisplayHomeAsUpEnabled(false) was not called explicitly and it was causing drawer icon to be hidden even when there were no items in the backstack so changing the setActionBarArrowDependingOnFragmentsBackStack() method worked for me.
private void setActionBarArrowDependingOnFragmentsBackStack() {
int backStackEntryCount = getSupportFragmentManager()
.getBackStackEntryCount();
// If there are no items in the back stack
if (backStackEntryCount == 0) {
// Please make sure that UP CARAT is Hidden otherwise Drawer icon
// wont display
getSupportActionBar().setDisplayHomeAsUpEnabled(false);
// Display the Drawer Icon
mDrawerToggle.setDrawerIndicatorEnabled(true);
} else {
// Show the Up carat
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
// Hide the Drawer Icon
mDrawerToggle.setDrawerIndicatorEnabled(false);
}
}
Logic is clear.
Show back button if fragment back stack is clear.
Show material hamburger-back animation if fragment stack is not clear.
getSupportFragmentManager().addOnBackStackChangedListener(
new FragmentManager.OnBackStackChangedListener() {
#Override
public void onBackStackChanged() {
syncActionBarArrowState();
}
}
);
private void syncActionBarArrowState() {
int backStackEntryCount = getSupportFragmentManager().getBackStackEntryCount();
mNavigationDrawerFragment.setDrawerIndicatorEnabled(backStackEntryCount == 0);
}
//add these in Your NavigationDrawer fragment class
public void setDrawerIndicatorEnabled(boolean flag){
ActionBar actionBar = getActionBar();
if (!flag) {
mDrawerToggle.setDrawerIndicatorEnabled(false);
actionBar.setDisplayHomeAsUpEnabled(true);
mDrawerToggle.setHomeAsUpIndicator(getColoredArrow());
} else {
mDrawerToggle.setDrawerIndicatorEnabled(true);
}
mDrawerToggle.syncState();
getActivity().supportInvalidateOptionsMenu();
}
//download back button from this(https://www.google.com/design/icons/) website and add to your project
private Drawable getColoredArrow() {
Drawable arrowDrawable = ContextCompat.getDrawable(getActivity(), R.drawable.ic_arrow_back_black_24dp);
Drawable wrapped = DrawableCompat.wrap(arrowDrawable);
if (arrowDrawable != null && wrapped != null) {
// This should avoid tinting all the arrows
arrowDrawable.mutate();
DrawableCompat.setTint(wrapped, Color.GRAY);
}
return wrapped;
}
If you take a look at the GMAIL app and come here to search for the carret/affordance icon..
I would ask you to do this, none of the above answer was clear. i was able to modify the accepted answer.
NavigationDrawer --> Listview contains subfragments.
subfragments will be listed like this
firstFragment == position 0 ---> this will have subfragments --> fragment
secondFragment
thirdFragment and so forth....
In firstFragment you have other fragment.
Call this on DrawerActivity
getFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
#Override
public void onBackStackChanged() {
if (getFragmentManager().getBackStackEntryCount() > 0) {
mDrawerToggle.setDrawerIndicatorEnabled(false);
} else {
mDrawerToggle.setDrawerIndicatorEnabled(true);
}
}
});
and in fragment
setHasOptionsMenu(true);
#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
activity.onBackPressed();
return true;
}
return false;
}
On the OnBackPressed Drawer activity method set the drawer toggle to true to enable navigation list icon again.
Thanks,
Pusp
You can look at this little example!
https://github.com/oskarko/NavDrawerExample
IMO, using onNavigateUp() (as shown here) in riwnodennyk's or Tom's solution is cleaner and seems to work better. Just replace the onOptionsItemSelected code with this:
#Override
public boolean onSupportNavigateUp() {
if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
// handle up navigation
return true;
} else {
return super.onSupportNavigateUp();
}
}