Grokking Android

Getting Down to the Nitty Gritty of Android Development

Adding ActionBarSherlock to Your Project

By

In my last post I had a look at how to set up a Fragment project that uses a master detail view. This week I am going to show you the necessary steps to make this project use the ActionBarSherlock library.

ActionBarSherlock is a library by Jake Wharton, that enables you to use action bars even on older devices without having to code an action bar from scratch. This library deals with all the details you do not want to care about. Even Googlers like Roman Nurik or Nick Butcher recommend to use this library and the Google IO app also makes use of this lib. So should you!

With this post I start my series about the action bar by simply adding an action bar to the project of my last post, adding some action items to it and explaining some basics of action bars. The focus of this post is mainly on getting ActionBarSherlock to run on your project. As such it is an introductory post about the action bar.

Since the action bar offers many functions and is the most important navigational component Android has, more posts about it will follow in the next weeks.

Getting ActionBarSherlock and adding it to Eclipse

The first thing you have to do, is to download the library from actionbarsherlock.com. After this unzip/untar it. The download contains three folders: library, samples and website. The samples folder contains four sample projects to showcase what you can do and to show you how to do it. You should have a look at them. The website folder contains the code of the project website. The library folder finally contains ActionBarSherlock's source code.

Now go to Eclipse and add the ABS-library as an Android project. Do not use Eclipse's import tool to import the ActionBarSherlock library - it would not work immediately and you would have to fix some settings. Instead use the project creation wizard of the Android Developer Tools.

Open File -> New -> Project -> Android Project From Existing Code.

Creating an Adnroid project from existing source
Creating an Android project from existing source

In the next screen select the folder, which Eclipse then uses to search for projects. If you select the ActionBarSherlock root folder, Eclipse suggests a list of projects to create. Leave the "library" project checked and uncheck all others:

Select the library project
Select the library project

Click "Finish" to create the project.

Eclipse will now create a new project named "library". I prefer a more useful name, so select the project called "library" and hit F2 to rename the project. I have renamed the project in Eclipse to "ActionBarSherlock", so all following screens will refer to this name.

Adding the library to your project

Now that ABS is a library project you have to tell your own project to use it. I will use the project of my last post for this. Go to FragmentStarter's project settings and switch to the Android tab.

If necessary scroll down until you see the Library panel. Click "Add":

Adding libraries to an Android project
Adding libraries to an Android project

In the next window all available library projects will be listed. Select ActionBarSherlock and click "Ok".

Select ActionBarSherlock from the list of libraries
Select ActionBarSherlock from the list of libraries

When the window disappears the library should be listed in the library panel of the Android properties tab:

Check if the library has been added succesfully
Check if the library has been added succesfully

What is slightly annoying is that the Android Developer Tools do not use the name of a project to reference it, but instead point to the directory itself. And what's even more annoying is that a symlink gets translated to its real path, which is bound to change more often than a symlink.

Should you ever want to change the directory, you have to delete the reference to the library with the old path and link to the newly imported library. But unless you do a fresh install for blog posts, this probably won't happen too often 🙂

That's it. Your project bundles the lib from now on.

But wait a moment! If you have a look at your project, you will notice that it now sports a red warning icon. Go to the error tab and you will see lots of warnings. Eclipse states for Fragment, FragmentActivity and all the other classes of the support library, that they "cannot be resolved to a type". Whoops!

Unknow type problems after adding ActionBarSherlock
Unknow type problems after adding ActionBarSherlock

The reason is, that ActionBarSherlock comes bundled with the library as well. And most of the time the support library added when following my post about fragments is different from the one bundled with ActionBarSherlock. In the console tab of Eclipse you see the message "Jar mismatch! Fix your dependencies" with the hashcodes of the mismatching files and where these files are stored:


[FragmentStarterProject] Found 2 versions of android-support-v4.jar in the dependency list,
[FragmentStarterProject] but not all the versions are identical (check is based on SHA-1 only at this time).
[FragmentStarterProject] All versions of the libraries must be the same at this time.
[FragmentStarterProject] Versions found are:
[FragmentStarterProject] Path: /opt/workspace/FragmentStarterProject/libs/android-support-v4.jar
[FragmentStarterProject] 	Length: 385685
[FragmentStarterProject] 	SHA-1: 48c94ae70fa65718b382098237806a5909bb096e
[FragmentStarterProject] Path: /opt/libs/ActionBarSherlock/library/libs/android-support-v4.jar
[FragmentStarterProject] 	Length: 349252
[FragmentStarterProject] 	SHA-1: 612846c9857077a039b533718f72db3bc041d389
[FragmentStarterProject] Jar mismatch! Fix your dependencies

To fix this, simply delete the support library from you project. Go to the libs folder, select the android-support-v4.jar file and delete it. You can still use the support library because it's also part of the ABS project.

If you need a newer version of the support library than the one bundled with ActionBarSherlock, remove the support library from both projects and add it anew to the ActionBarSherlock project.

With this done your project is good again. The next step, of course, is to change some code.

Changing the types to their ActionBarSherlock counterparts

Just adding the library won't magically add an action bar to your project. Instead you have to change some of your code.

The first thing you have to do, is to change the classes from which you inherit. Instead of Activity use SherlockActivity, instead of FragmentActivity use SherlockFragmentActivity, instead of Fragment use SherlockFragment and so on. Now do this for the two activities and the two fragments you created while reading the last post.

You will notice that the ItemDetailActivity won't compile any more. Whenever you add ActionBarSherlock to an existing project, this is bound to happen. Why is that?

Have a look at the error message Eclipse is displaying: "Cannot override the final method from SherlockFragmentActivity". The method is the following:


@Override
public boolean onOptionsItemSelected(MenuItem item) {
   // ...
}

ActionBarSherlock overrides every method of it's superclasses that takes either a Menu, MenuItem or MenuInflater object of the android.view package as parameter and declares those methods as final. You need to know that all the items in an action bar are actually menu items. Thus only by doing it this way ActionBarSherlock can take control of all the menu-handling and change it for older Android versions.

While the error message might sound bad, it actually isn't. ActionBarSherlock provides
for every final method another method with the same name and the same number of arguments. Even the class names of the arguments are the same. Only the packages differ.

This way ActionBarSherlock makes migration of existing projects very easy. You can keep your methods and do not have to change and adjust any logic. Simply delete all import statements to the menu-related classes of the package android.view. After this hit Ctrl-O to optimize and correct the import statements and when Eclipse displays the list of imports to choose from, choose those of the ActionBarSherlock library:

Choose ActionBarSherlock's classes for imports
Choose ActionBarSherlock's classes for imports

If you do this for ItemDetailActivity the warnings disappear. Whenever you use Eclipse (or any other IDE for that matter) to generate the import statements for you, take care to select the types of the ActionBarSherlock library. Those all start with com.actionbarsherlock.

Now that your code looks fine, you should try to run the project. Whether this run is successful or not depends on the Android version of your device. If you run this code on a device with at least Android 4.0 everything runs fine. But not so on older devices. Here you will get a Force Close dialog due to an IllegalStateException:


java.lang.IllegalStateException: 
      You must use Theme.Sherlock, 
      Theme.Sherlock.Light, 
      Theme.Sherlock.Light.DarkActionBar, 
      or a derivative.

Gladly the exception is pretty specific: You have to use one of the Sherlock themes to get your project to run. ActionBarSherlock needs many definitions to get the action bar styled correctly. Thus it needs these styles and you have to use the ABS themes. You can use your own theme, of course, but you must use one of Sherlock's themes as a base for yours. See the Sherlock theming guide for more information on this.

Since adding the action bar to older devices is the reason to use ActionBarSherlock in the first place, you have to change the theme. Change your AndroidManifest.xml file to use a Sherlock theme:


<application
   android:allowBackup="true"
   android:icon="@drawable/ic_launcher"
   android:label="@string/app_name"
   android:theme="@style/Theme.Sherlock.Light" >
   <!-- ... -->
</application>

Now finally the app runs fine. And yes, there is an ActionBar. As you can see it works fine on current as well as on older devices:

Initial action bar on a 2.1 device
Initial action bar on a 2.1 device
Initial action bar on a 4.2 device
Initial action bar on a 4.2 device

Adding items to the ActionBar

The action bar is basically what the old menu was before Honeycomb. This means that you have to create a menu to see an action bar.

Create a file with the name activity_itemlist.xml in the folder /res/menu and add the following code to it. This file describes the structure of your action bar:


<menu xmlns:android=
      "http://schemas.android.com/apk/res/android" >

    <item
        android:id="@+id/add_item"
        android:icon="@drawable/ic_action_add"
        android:showAsAction="ifRoom"
        android:title="@string/add_item"
        android:titleCondensed="@string/add_item">
    </item>
    <item
        android:id="@+id/about"
        android:icon="@drawable/ic_action_info"
        android:showAsAction="never"
        android:title="@string/about"
        android:titleCondensed="@string/about">
    </item>

</menu>

Note: I use icons from the icon pack "Android Developer Icons 2" of opoloo. The values I use for the android:icon attributes are specific to this icon pack. As soon as you want to try this code, you have to prepare icons that match this code or change the attribute value to fit your icons. See the appendix for more on this.

Only the xml file won't suffice. You also need to override the onCreateOptionsMenu() method to inflate this file:


@Override
public boolean onCreateOptionsMenu(Menu menu) {
   MenuInflater inflater = getSupportMenuInflater();
   inflater.inflate(R.menu.activity_itemlist, menu);
   return true;
}

This is the result when you run the project again. Note the differences between the 2.1 device and the 4.2 device:

Action bar with icons running on a 2.1 device
Action bar with icons running on a 2.1 device after pressing the menu button
Action bar with icons running on a 4.2 device
Action bar with icons running on a 4.2 device

The overflow menu

When you compare the screenshots of the previous section, you quickly understand what the so-called overflow menu is. This is the menu hidden behind the three dots are the far end of the action bar on modern devices.

Android shows as many items as it can directly on the action bar. But especially on phones the place for action items is quite limited. Thus Android shows all those items, that do not fit on the action bar directly, in the overflow menu. You can also tell Android to put items in the overflow menu regardless of the space. You can do so by using the value never for the attribute android:showAsAction of these items. More on this attribute later on.

The items of the overflow menu are only visible if you click the three dots on the right of the action bar or press the menu button on those devices that have such a button.

The three dots appear only on devices without a menu button. That's the way to indicate to the user that more items are available and it's also the only way how users can access these items. On devices with a menu button on the other hand you do not have any indicator, but users can always check if an overflow menu is present by clicking the menu key. I actually think the solution for devices without menu button is way better than the solution on devices with a menu button. On these latter devices users always have to guess if an overflow menu is present or not. Alas Samsung, the vendor that sells by far the most Android devices, still ships devices with menu buttons 🙁

Sort your items by importance so that the most important actions are visible all the time. Those actions are the ones your users are most likely to perform on this screen. Think thoroughly about the importance of each item.

Other items should always be in the overflow menu, no matter how much space is left. For example if your app has an about screen, or some license info for the open source libs you use (well, ActionBarSherlock for example) or some information about what has changed with the recent version, I would put all those items into the overflow menu - no matter how much screen estate you have left.

The Android design page has some more guidance on this on their action bar pattern page and also shows you roughly how many items fit on the screen for some selected devices.

Use the android:icon as well as the android:title attribute

As you can see the action bar shows only an icon for the "Add Items" action. But that doesn't mean that you should neglect the strings. First of all these strings are used for screenreaders so that visually impaired people still can use your app. And secondly, if users do not understand an icon immediately, they can longpress those icons until a hint appears that displays the text. Of course this hint is no excuse for confusing icons!

Another thing to note. Overflow menu items show only text. But not so on older devices. There you see the icon and the text. So always provide values for android:icon and for android:title!

Use android:showAsAction to define the appearance of the action items

The item element of the xml file for the action bar can contain four attributes that are only relevant for the action bar: android:showAsAction, android:actionLayout, android:actionViewClass and android:actionProviderClass. Of these I describe only the frst here, the other three are a topic for an extra post.

You can use the attribute android:showAsAction to decide which icons to display and in which way. It can take the following five values:

Possible values for the android:showAsAction attribute
Value Meaning
ifRoom Display item if enough room is left for this item
always Display this item, regardless of the existing place
never This item is not displayed in the action bar directly but in the overflow menu
withText Display the icon together with the title text
collapseActionView Only relevant for collabsible action views - not covered in this post

As a rule of thumb you should always use ifRoom, if you want the icon to be part of your action bar and you should use never if you want the item to always be part of the overflow menu.

The Android documentation for menu resources explicitly urges you to use always with great care. You should use this attribute only if the element is absolutely necessary. If you use this on multiple items and the place in the action bar is too small to do this properly Android displays overlapping icons. In this case the items are even less accessible than those of the overflow menu.

While you have to pick one of the first three, you can combine these with the value withText. This value decides whether Android shows only the icon or the icon plus the text of the android:title attribute. But Android does show the text only, if enough room is left.

As an example change the menu file to the following code:


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

Now these are the resulting screens depending on the Android version:

Action bar with multiple icons running on a 2.1 device
Action bar with multiple icons running on a 2.1 device
Action bar with multiple icons running on a 4.2 device
Action bar with multiple icons running on a 4.2 device after selecting the overflow menu

As you can see on phones Android still only displays the icons. Note what happens on small devices that have no menu button. Android had to put the second item into the overflow menu because it had not enough place for it. On devices with a menu button the second item would be directly visible in the action bar.

This is what the same app looks like on a tablet:

Action bar with multiple icons and text running on a 4.2 tablet
Action bar with multiple icons and text running on a 4.2 tablet

Now the withText value makes a difference. While I like items to display text on tablets, it is very unusual. Have a look at other apps to see how they do it, before you decide whether to display text or not. But do not use text to gloss over weaknesses in your icon design.

Dealing with user input

So far you have created the action bar and made it look right. But when you click on an item, nothing happens.

What you have to do is to implement the onOptionsItemSelected() method. You do this as you always did for menu item selections:


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

Action bar interaction is like any other user interaction and all the usual rules about the UI thread apply. So don't put any stuff in here that's long-lasting. Use Loaders, AsyncTasks, IntentServices or whatever you prefer for the specific job to do the heavy lifting.

What's next?

In this first part of the ActionBar tutorial you have changed your project so that your activities and fragments now inherit from ActionBarSherlock's classes and then made it compile and run.

After this you added items to the action bar, learned about the overflow menu and about the ActionBar-related attributes of the menu xml files. But with this you barely scratched the surface of what the action bar covers.

According to the design guidelines the "action bar is one of the most important design elements you can implement". No wonder it features all sorts of possibilities. And, yes, it brings with it some complexity as well. So there is more to the action bar than just this post.

In the next post I am goig to cover the different navigation types the action bar supports. After this I will write yet another post about the so-called contextual action bar.

Appendix

As mentioned all icons that I use are icons of opoloo's Android Developers Icons 2. You have to either get this pack, create icons for yourself or download the "Action Bar Icon Pack" from the design site. The latter icon pack contains no "Add" icon, so you have to adapt the XML.

You can also use totally inappropriate icons and hassle with the design later on. For the sake of following this tutorial you could use something like @android:drawable/ic_menu_add. But you should never use these for anything else than for just getting on with this tutorial.

You have to add the icons you want to use to the respective folders within your /res folder. For details on how to name these folders see the Android documentation about providing resources.

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.