Grokking Android

Getting Down to the Nitty Gritty of Android Development

Adding ActionBar Items From Within Your Fragments

By

This tutorial shows you how to add action items to the ActionBar from within your Fragments and how to disable or enable action items depending on the state of your fragment. All code works with the stock Android ActionBar object as well as with ActionBarSherlock.

Why do you want to add action items?

In a previous post you have seen that Android makes it easy to provide responsive interfaces. And given the vast distribution of devices sizes that is something you should do.

This post continues with the app, you have developed in the first posts of this series. The user has a list of items, a detail screen and an edit screen.

On a phone the startscreen simply shows a list of items. Here you have an action item to add a new item to the list:

Screen showing the ListFragment only
Screen showing the ListFragment only

The detail screen shows all the relevant details of this item and offers you to edit the item:

Screen showing the DetailFragment only
Screen showing the DetailFragment only

Now here's what the tablet version looks like:

Top part of the tablet version containing both fragments
Top part of the tablet version containing both fragments

As you can see, it now sports both icons since it also presents both fragments. Now this app is pretty dull so far, and the detail screen even more so, so let me show you a real app. I take Timetable, an app that does this nicely.

Timetable app showing action items of multiple fragments
Timetable app showing action items of multiple fragments

In this screen the edit and trash icon are from the selected element and are only added when a lecture has been selected. The plus and the search icon though are from the ListFragment.

Adding action items programatically

As the post title implies the additional action items are inserted by the fragment. In fact not much different from how you add the items within your Activities. You use the onCreateOptionsMenu() method for it:


@Override
public void onCreateOptionsMenu(
      Menu menu, MenuInflater inflater) {
   inflater.inflate(R.menu.activity_itemdetail, menu);
}

As you can see the signature of this method is different from the one of the Activity class. With fragments your method takes a MenuInflater as second parameter. And the fragment's onCreateOptionsMenu() method also doesn't return a boolean value.

Even with this method in place Android still doesn't display any additional menu items unless you explicitly tell it to call this method. To do so, you first have to call setHasOptionsMenu() within the fragment's onCreate() method:


setHasOptionsMenu(true);

Now your fragments menu items will be displayed in the ActionBar.

Of course, if you do so, you have to stop using the same entries in your Activity. Otherwise it would look fine on any screen that makes use of your multi-pane layouts but you would end up with doubled icons on single-pane screens.

For the sample screen I have used a menu xml file containing only the about entry in the activity. And I use an xml file containing only the edit entry in the fragment. The about entry is not needed in the fragment, because it is added by the activity, no matter whether you are in single-pane or double-pane mode.

Changing the order of action items

By default Android displays the action items of your fragment behind those of the activity. If you use multiple fragments Android displays them in the order the fragments are created.

This default order is not necessarily what you want. For example it is very likely that you want to display an about item as the very last entry within your overflow-menu - no matter if fragments later on add more overflow-menu entries. Also some actions are more often used than others. You want to put them more to the left, so that, depending on the screen size, those are always visible. And which items are more important depends on which fragments are in use.

Tio help you achieve this you can make use of the android:orderInCategory attribute of the item elements.

The fragment_itemdetail.xml menu file:


<menu xmlns:android="http://schemas.android.com/apk/res/android" >
   <item
      android:id="@+id/edit_item"
      android:icon="@drawable/ic_action_edit"
      android:showAsAction="ifRoom"
      android:orderInCategory="10"
      android:title="@string/edit_item">
   </item>
</menu>

The activity_itemdetail.xml menu file:


<menu xmlns:android="http://schemas.android.com/apk/res/android" >
   <item
      android:id="@+id/about"
      android:icon="@drawable/ic_action_info"
      android:showAsAction="never"
      android:orderInCategory="9999"
      android:title="@string/about">
   </item>
</menu>

And finally the activity_itemlist.xml menu xml file:


<menu xmlns:android=
      "http://schemas.android.com/apk/res/android" >
   <item
      android:id="@+id/add_item"
      android:icon="@drawable/ic_action_add"
      android:orderInCategory="20"
      android:showAsAction="ifRoom"
      android:title="@string/add_item">
   </item>
   <item
      android:id="@+id/about"
      android:icon="@drawable/ic_action_info"
      android:showAsAction="never"
      android:orderInCategory="9999"
      android:title="@string/about">
   </item>
</menu>

The higher the number the later your action item is displayed. So use an absurdly high value for the about item. And order all other elements by importance.

Since all elements have an order value of zero by default, you have to use numbers for all action items as soon as you want to move one to the front.


android:orderInCategory="9999"

Handling events

As usual you can handle menu item clicks in the onOptionsItemSelected() method:


@Override
public boolean onOptionsItemSelected(MenuItem item) {
   // handle item selection
   switch (item.getItemId()) {
      case R.id.edit_item:
         // do s.th.
         return true;
      default:
         return super.onOptionsItemSelected(item);
   }
}

But bear in mind that the Activity's onOptionsItemSelected() method is called first. Your fragment's method is called only, when the Activity didn't consume the event!

Adding and removing action items

So far I have only mentioned how to use action items, that are defined within an xml file. But that often is not sufficient.

See this screenshot of a German TV program app. Please ignore, that it's in German, I explain what is relevant to you.

The Prime Guide app showing icons only when relevant
The Prime Guide app showing icons only when relevant

In this app you can select a program to get more information about it. If you select a program that hasn't started yet, you can bookmark it and set an alarm for it. If on the other hand the program either has already started or ended those actions are not displayed. It simply makes no sense to add an alarm for some program that has already started. But all other icons are visible in both cases.

So how to do that? You could either use different menu xml files for a static set of action items. Or you could add or remove menu actions programatically. Since the second approach is more flexible I deal with it in the next paragraphs.

First you need to get hold of a Menu object. You can do so in the onCreateOptionsMenu() method shown above.

Now you can use this Menu object to either change the visibility of action items or to remove them from the menu entirely.

You can change the visibility like this:


if (mMenu != null) {
   mMenu.findItem(R.id.edit_item).setVisible(false);
}

The null check in the code is necessary, because of the order in which the methods of your fragment are called. Of course Android executes the lifecycle methods before finally calling onCreateOptionsMenu(). Thus your Menu object is null in the onCreateView() method, which you probably use to create event handlers.

If you want to entirely remove the action item, you can do so easily as well:


if (mMenu != null) {
   mMenu.removeItem(R.id.edit_item);
}

As you will see in the next few paragraphs, adding a menu item is slightly more cumbersome than just making it visible again. Thus if you want to toggle action items depending on the state of your fragment, I suggest to change the visibility instead of removing and re-adding the items.

So far I have only shown you how to get rid of action items. Of course you also can add new items. You can use one of the several add() methods of the Menu class. For example the following code fragment would add the item in the same way, as with the xml code for inflating it:


MenuItem item = 
      mMenu.add(Menu.NONE, R.id.edit_item, 10, R.string.edit_item);
item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
item.setIcon(R.drawable.ic_action_edit);

The first parameter sets the group. If you have no groups, use Menu.NONE. The second parameter sets the id you want to use. It's the same one you later use in the onOptionsItemSelected() method to react to menu item clicks. The third parameter is the order value and the last one the title text you want to use. Remember, that Android uses the title text to display usage hints or to read out this value with a screenreader app. So never use null for this value.

As the code sample shows, you have to add the icon and the showAsAction attribute value with separate method calls later on.

I don't know why there is no way to add a complete MenuItem object directly. To me that would be better readable. But that's just a tiny detail 🙂

Wrapping up

In this post I have shown that you can use the onCreateOptionsMenu() method to add action items related to your fragment to the existing icons of the ActionBar.

Since the default order in which Android adds these items is often not the preferred one, you can change this easily by adding the android:orderInCategory attribute to your menu items in the xml files.

Finally you have seen how to change the visibility of menu items, how to remove them completely or how to add new menu items.

Please let me know in the comments, if any questions are left open and which way you prefer to remove action items from the ActionBar. And don't forget to plus one and share this post if you found it useful!

Wolfram Rittmeyer lives in Germany and has been developing with Java for many years.

He has been interested in Android for quite a while and has been blogging about all kind of topics around Android.

You can find him on Google+ and Twitter.