Grokking Android

Getting Down to the Nitty Gritty of Android Development

Migrating from ActionBarSherlock to ActionBarCompat

By

In July 2013 Google announced ActionBarCompat as part of its support library package. This library makes it easy to use the Action Bar that have to support older devices. Many have waited for this to happen ever since the Action Bar was introduced with Honeycomb.

I, though, didn't expect that to happen. After all the Action Bar was announced long ago and we had ActionBarSherlock. So why all off a sudden?

I have written multiple posts about ActionBarSherlock on this blog prior to this announcement. Those posts still get a lot of attention and search hits. But while the basic concepts still apply, any ActionBarSherloc specific parts are kind of deprecated 🙂

I strongly believe that you should use ActionBarCompat for all new projects that want to support older devices. It also might make sense to migrate existing projects. So read on to learn why you should migrate or use ActionBarCompat right away and how to migrate existing projects.

Why you should prefer ActionBarCompat over ActionBarSherlock

There are many reasons why you should prefer ActionbarCompat over ActionbarSherlock.

First of all this project is by Google, is part of the Support Library and thus likely will support new Action Bar related stuff at the same time Google releases them with stock Android.

Another good reason and a proof for the previous point is that ActionBarCompat supports the Navigation Drawer pattern right out of the box, while ActionBarSherlock does not. Thus if you want to add this drawer to an existing project/app you should migrate.

The last and not least reason is, that the creator of ActionBarSherlock, Jake Wharton, announced on Google+ that further development of ActionBarSherlock has been stopped. ActionBarSherlock 4.4 is the last release and might get bug fixes - but there won't be any new features:

While there may be a dot release or two in the coming weeks, version 4.4 is shaping up to be The Last Releaseâ„¢.

He's not too sad either 🙂  See this quote, Jake Wharton made on his blog about ActionBarSherlock being intended to be thrown away eventually:

This is the entire purpose of the library. It has been designed for this use case specifically. It has been deprecated from inception with the intention that you could some day throw it away.

Sample project

To showcase how the migration works, I have migrated the sample project ActionViews to ActionBarCompat. This sample project shows how to use the Action Bar with older devices and as a special case how to use ActionViews within your Action Bar. You can find more information about ActionViews on my older post. While originally written with ActionBarSherlock in mind, you can easily transfer those code samples to ActionBarSherlock. The next sections show you how to adapt the code snippets to move from ActionBarSherlock to ActionBarCompat.

You can find the ActionViews sample project on bitbucket. If you like you can see all changes necessary for the ActionBarCompat migration by viewing the commit.

Of course you have to integrate ActionBarCompat in the IDE of your choice. I do not cover this here. Gabriele Mariotti has written an excellent post covering the IDE integration of ActionBarCompat. I originally developed this project using Eclipse - which is why I occasionally refer to Eclipse specific shortcuts or why I include an Eclipse dialog later on. But the core of this post is independent of the IDE you want to use.

How to proceed

So I assume that you have removed ActionBarSherlock from the ActionViews project and added ActionBarCompat instead. After that your project should be plagued with errors. Which is not too surpising. In the next sections I will take you through all the steps needed to get the project deployable again.

I start with the resources and deal with code changes later on. Android's Development Tools only generate a new R.java file when the resources are error-free. And without a correct R file your Java sources won't compile properly. Thus I prefer to fix resources first.

Changing the style

The first thing to do, is to correct the style. I have used an ActionBarSherlock style previously. This style has to change to the proper ActionBarCompat style. And as with ActionBarSherlock you have to use an ActionBarCompat style, otherwise you'll get a RuntimeException at app start.

Change the style with the name AppBaseTheme to use Theme.AppCompat.Light.DarkActionBar as its parent:


<style name="AppBaseTheme" 
      parent="@style/Theme.AppCompat.Light.DarkActionBar">
   <!-- nothing API level dependent yet -->
</style>

Since you might forget this step in future projects - at least I'm prone to initially forget this step - here's the Exception that you will see in logcat in that case. Having seen it here might help you to remember what to do about it.


E/AndroidRuntime( 1413): Caused by: java.lang.IllegalStateException: You need to use a Theme.AppCompat theme (or descendant) with this activity.
E/AndroidRuntime( 1413): 	at android.support.v7.app.ActionBarActivityDelegate.onCreate(ActionBarActivityDelegate.java:111)
E/AndroidRuntime( 1413): 	at android.support.v7.app.ActionBarActivityDelegateICS.onCreate(ActionBarActivityDelegateICS.java:58)
E/AndroidRuntime( 1413): 	at android.support.v7.app.ActionBarActivity.onCreate(ActionBarActivity.java:98)
E/AndroidRuntime( 1413): 	at com.grokkingandroid.sampleapp.samples.actionbar.actionviews.BaseActivity.onCreate(BaseActivity.java:30)

The dev tools will highlight the attribute android:textIsSelectable somewhere up in the file after you save it. That's because the minimum API level of the project is SDK 7 and the attribute has only been introduced with SDK 11. You can safely ignore this. If you clean your project after some more fixes, this marker will disappear. You won't get any problems with lower versions despite this.

Fixing the menu definitions

The next step is to correct the menu definition files. Older Android versions don't know about the Action Bar and thus do not support those new xml attributes for the Action Bar. So you have to change all attributes with the name android:showAsAction, android:actionViewClass, android:actionProviderClass, or android:actionLayout. The fix is easy. Simply change the namespace from android to app and add this namespace.

For example for the file menu_fragment_expandable.xml the new xml looks like this:


<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <item
        android:id=
           "@+id/actionViewLayout"
        app:actionLayout=
           "@layout/expandable_actionview_edittext"
        android:icon=
           "@drawable/ic_action_add_inverse"
        app:showAsAction=
           "ifRoom|collapseActionView"
        android:title=
           "@string/add_item"/>
</menu>

I make the namespace known to the parser in line 3. I use this namespace for the Action Bar specific attributes in lines 6 and 8. This way ActionBarCompat can read those attributes and provide the correct appearance of the menu items.

Fixing the Searchview to use

Have a look at the menu_fragment_search.xml file. Neither your IDE nor Android's Lint checker will flag this file as incorrect. Yet it is.


<menu xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:app="http://schemas.android.com/apk/res-auto">
   <item
      android:id=
         "@+id/searchView"
      app:actionViewClass=
         "com.actionbarsherlock.widget.SearchView"
      android:icon=
         "@drawable/ic_action_search_inverse"
      app:showAsAction=
         "ifRoom|collapseActionView"
      android:title=
         "@string/search"/>
</menu>

Obviously line 7 won't work any longer. Instead of Sherlock's SearchView you have to use the SearchView of the ActionBarCompat library. You now have to use the class android.support.v7.widget.SearchView. The updated file should look like this:


<menu xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:app="http://schemas.android.com/apk/res-auto">
   <item
      android:id=
         "@+id/searchView"
      app:actionViewClass=
         "android.support.v7.widget.SearchView"
      android:icon=
         "@drawable/ic_action_search_inverse"
      app:showAsAction=
         "ifRoom|collapseActionView"
      android:title=
         "@string/search"/>
</menu>

Should you forget this step in a project of yours, you are going to see an exception in logcat:


W/SupportMenuInflater( 1308): Cannot instantiate class: com.actionbarsherlock.widget.SearchView
W/SupportMenuInflater( 1308): java.lang.ClassNotFoundException: Didn't find class "com.actionbarsherlock.widget.SearchView" on path: DexPathList[[zip file "/data/app/com.grokkingandroid.sampleapp.samples.actionbar.actionviews-2.apk"],nativeLibraryDirectories=[/data/app-lib/com.grokkingandroid.sampleapp.samples.actionbar.actionviews-2, /system/lib]]
W/SupportMenuInflater( 1308): 	at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
W/SupportMenuInflater( 1308): 	at java.lang.ClassLoader.loadClass(ClassLoader.java:497)
W/SupportMenuInflater( 1308): 	at java.lang.ClassLoader.loadClass(ClassLoader.java:457)
W/SupportMenuInflater( 1308): 	at android.support.v7.internal.view.SupportMenuInflater$MenuState.newInstance(SupportMenuInflater.java:480)
W/SupportMenuInflater( 1308): 	at android.support.v7.internal.view.SupportMenuInflater$MenuState.setItem(SupportMenuInflater.java:441)
W/SupportMenuInflater( 1308): 	at android.support.v7.internal.view.SupportMenuInflater$MenuState.addItem(SupportMenuInflater.java:462)
W/SupportMenuInflater( 1308): 	at android.support.v7.internal.view.SupportMenuInflater.parseMenu(SupportMenuInflater.java:196)
W/SupportMenuInflater( 1308): 	at android.support.v7.internal.view.SupportMenuInflater.inflate(SupportMenuInflater.java:118)
W/SupportMenuInflater( 1308): 	at com.grokkingandroid.sampleapp.samples.actionbar.actionviews.SearchViewFragment.onCreateOptionsMenu(SearchViewFragment.java:40)

Whenever you migrate an existing project, I suggest you run a search over all sources - especially the resources. Search for "sherlock" to find out, if you've missed any references. Another good candidate for problems is be the manifest file if you previously used ActionBarSherlock styles directly within it.

If you have corrected all menu files, you are done with the resources. Select Project -> Clean to clean the actionviews project and get rid of the android:textIsSelectable error marker. The Android Dev Tools generate a new R.java file and it's time to have a look at the Java sources.

Fixing fragments

I'm going to show you first how to fix fragments and later on how to fix activities.

Substituting SherlockDialogFragment with DialogFragment

The first and most simple one to fix is the AboutFragment. You have to fix the inheritance since this class inherits from an ActionBarSherlock class which you want to migrate away from. Simply substitute SherlockDialogFragment with DialogFragment and the AboutFragment is fine. Of course you have to correct the imports by hitting Ctrl-O.

Note: Eclipse asks you whether to use an import from stock Android or from the support library. Whenever eclipse offers you this alternative, you always have to use the imports of the support library:

Select the support library versions whenever this choice is presented to you
Select the support library versions whenever this choice is presented to you

Substitute SherlockListFragment with ListFragment

Next up is the SearchFragment. It inherits from SherlockListFragment. Replace this with the stock ListFragment and hit Ctrl-O.

Replace getSherlockActivity() with getActivity()

Two red markers remain - both for the method call to get an activity object. Simply replace getSherlockActivity() with getActivity() and the code is fine again.

Substitute SherlockFragment with Fragment

Now to those other fragments that make up the beef of the sample. Those fragments all inherit from the base class DemoBaseFragment. Again you have to change the type of the super class. Replace SherlockFragment with the stock Fragment class of the support library. Afterwards hit Ctrl-O and change the getSherlockActivity() calls to getActivity() calls as mentioned above.

For the four remaining fragments change the imports first. ActionBarCompat needs no special Menu or MenuItem objects like ActionBarSherlock did. Thus two of the four fragments are immediately correct after fixing the imports. Those other two are a tiny bit more involved.

Use MenuItemCompat for ActionViews and ActionProviders

The problem is with ActionViews - the core content of the sample app. ActionBarCompat uses plain Menu objects. But those do not know of ActionViews prior to API level 11. So ActionBarCompat uses a helper class named MenuItemCompat to deal with ActionViews and ActionProviders.

Instead of calling getActionView() on a MenuItem object you have to use MenuItemCompat.getActionView(). For example the new way to get hold of the ActionView in the SearchViewFragment looks like this:


SearchView searchView = 
      (SearchView)MenuItemCompat.getActionView(item);

The same applies to setting an ActionView. The PlaceholderActionViewFragment for example uses the following lines:


MenuItemCompat.setActionView(
      PlaceholderActionViewFragment.this.mProgress, 
      R.layout.actionview_progress);

For ActionProviders you would have to do the same - just with different method names of course.

Fixing activities

As with fragments you have to change the super classes for the activities as well. The actionviews project uses one base activity for all activities within the project. This class obviously is the starting point for changes to activities.

Replace SherlockFragmentActivity with ActionBarActivity

Changing the super class for activities is slightly different to the changes you applied to fragments in the previous steps. With fragments you simply change them to stock fragments of the support library. With activities on the other hand you have to use a super class of the ActionBarCompat library.

Open BaseActivity and change the super class from SherlockFragmentActivity to ActionBarActivity.

Replace getSupportMenuInflater() with getMenuInflater()

One problem remains after fixing the imports. The first problem is the call to getSupportMenuInflater(). Simply replace it with getMenuInflater() instead.

Replace Sherlock layout IDs

The next activity you are going to fix is the ActionViewActivity. After fixing all imports there are still two red markers left. Those are for sherlock specific layout resources.

First replace R.layout.sherlock_spinner_item with android.R.layout.simple_spinner_item. Next replace sherlock_spinnner_dropdown_item with R.layout.support_simple_spinner_dropdown_item. Edit: Thanks to George for his correction of my original recommendation (see his comment below).

The respective lines should look like this after you applied those changes:


ArrayAdapter<CharSequence> spinnerAdapter = 
      ArrayAdapter.createFromResource(
            getSupportActionBar().getThemedContext(), 
            resId, 
            android.R.layout.simple_spinner_item);
spinnerAdapter.setDropDownViewResource(
      R.layout.support_simple_spinner_dropdown_item);

For the two remaining activities, which I didn't cover yet, you simply have to fix the imports to make them compile again. After that your project should contain no more errrors and should build fine.

Lessons learned

After reading this post you should be able to migrate an existing project which is based on ActionBarSherlock to ActionBarCompat. You have seen good reasons of why to do this and the steps to follow to get your project running again.

There are other great posts about ActionBarCompat out there. Mark Allison wrote a series about ActionBarCompat and Antonio Leiva also has a three part series on ActionBarCompat. The last post also covers migrating from ActionBarSherlock. And Gabriele Mariotti also has multiple posts about ActionBarCompat. We all use different approaches - so look for the post style you like the most. If you feel important parts are missing here, have a look at these other posts.

Till next time. Happy Coding - and Happy Migrating!

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.