07 agosto 2013
Posted by Chris Banes, Android Developer Relations
Following on the Android 4.3 and Nexus 7 announcements, we recently released two important tools to help you build beautiful apps that follow Android Design best practices:
This post helps you get started with ActionBarCompat, from setting up the Support Library to adding an Action Bar to your UI. If you want to see ActionBarCompat in action first, download the I/O 2013 app from Google Play and give it a try. The app's Action Bar is built using the ActionBarCompat and served as our main test environment for making sure the APIs work properly, with full compatibility across platform versions.
ActionBarCompat is a new API in the Android Support Library that allows you to add an Action Bar to applications targeting minSdkVersion
7 or greater. The API and integration have been designed to closely mirror the existing framework APIs, giving you an easy migration path for your existing apps.
If you've already using another implementation of Action Bar in your app, you can choose whether to ActionBarCompat. If you’re using the old ActionBarCompat
sample available through the SDK, then we highly recommend that you upgrade to the new ActionBarCompat API in the Support Library. If you’re using a third-party solution (such as ActionBarSherlock), there are a few reasons to consider upgrading:
Menu
and MenuItem
classes.Fragment
class.ActionBarDrawerToggle
for use with DrawerLayout
.PopupMenu
.ActionBarSherlock is a solid and well-tested library which has served developers very well for a long time. If you are already using it and do not currently require any of the above then there is no need to migrate.
If you are ready to get started with ActionBarCompat, the sections below take you through the process.
ActionBarCompat is distributed as both a Gradle artifact and a library project. See Setting Up the Support Library for more information.
The first thing to update are your Activity
themes so they use a Theme.AppCompat
theme. If you're currently using one of the Theme.Holo
themes then just replace this with the related Theme.AppCompat
theme. For instance if you have the following in your AndroidManifest.xml
:
<activity ... android:theme="@android:style/Theme.Holo" />
You would change it to:
<activity ... android:theme="@style/Theme.AppCompat" />
Things get more tricky when you have a customized theme. The first thing to do is change your base theme to one of the Theme.AppCompat
themes, like above.
If you have customized styles for the Action Bar (and related widgets) then you must also change these styles to extend from the Widget.AppCompat
version. You also need to double-set each attribute: once in the Android namespace and again with no namespace (the default namespace).
For example, in the following example we have a custom theme which extends from Theme.Holo.Light
, which references a custom Action Bar style.
<style name="Theme.Styled" parent="@android:style/Theme.Holo.Light"> <item name="android:actionBarStyle">@style/Widget.Styled.ActionBar</item> </style> <style name="Widget.Styled.ActionBar" parent="@android:style/Widget.Holo.Light.ActionBar"> <item name="android:background">@drawable/ab_custom_solid_styled</item> <item name="android:backgroundStacked" >@drawable/ab_custom_stacked_solid_styled</item> <item name="android:backgroundSplit" >@drawable/ab_custom_bottom_solid_styled</item> </style>
To make this work with AppCompat you would move the above styles into the values-v14
folder, modifying each style's parent to be an AppCompat version.
<style name="Theme.Styled" parent="@style/Theme.AppCompat.Light"> <!-- Setting values in the android namespace affects API levels 14+ --> <item name="android:actionBarStyle">@style/Widget.Styled.ActionBar</item> </style> <style name="Widget.Styled.ActionBar" parent="@style/Widget.AppCompat.Light.ActionBar"> <!-- Setting values in the android namespace affects API levels 14+ --> <item name="android:background">@drawable/ab_custom_solid_styled</item> <item name="android:backgroundStacked" >@drawable/ab_custom_stacked_solid_styled</item> <item name="android:backgroundSplit" >@drawable/ab_custom_bottom_solid_styled</item> </style>
You would then need add the following into the values
folder:
<style name="Theme.Styled" parent="@style/Theme.AppCompat.Light"> <!-- Setting values in the default namespace affects API levels 7-13 --> <item name="actionBarStyle">@style/Widget.Styled.ActionBar</item> </style> <style name="Widget.Styled.ActionBar" parent="@style/Widget.AppCompat.Light.ActionBar"> <!-- Setting values in the default namespace affects API levels 7-13 --> <item name="background">@drawable/ab_custom_solid_styled</item> <item name="backgroundStacked">@drawable/ab_custom_stacked_solid_styled</item> <item name="backgroundSplit">@drawable/ab_custom_bottom_solid_styled</item> </style>
As with the standard Action Bar in Android 3.0 and later versions, action items are added via the options menu. The difference with ActionBarCompat is that you need to modify your Menu
resource files so that any action item attributes come from ActionBarCompat's XML namespace.
In the example below you can see that a new "yourapp" namespace has been added to the menu
element, with the showAsAction
and actionProviderClass
attributes being provided from this namespace. You should also do this for the other action item attributes (actionLayout
and actionViewClass
) if you use them.
<menu xmlns:yourapp="http://schemas.android.com/apk/res-auto" xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@+id/menu_share" android:title="@string/menu_share" yourapp:actionProviderClass="android.support.v7.widget.ShareActionProvider" yourapp:showAsAction="always" /> ... </menu>
ActionBarCompat contains one Activity class which all of your Activity classes should extend: ActionBarActivity
.
This class itself extends from FragmentActivity
so you can continue to use Fragments in your application. There is not a ActionBarCompat Fragment class that you need to extend, so you should continue using android.support.v4.Fragment
as the base class for your Fragments.
To display items on the Action Bar, you need to populate it by overriding onCreateOptionsMenu()
and populating the Menu
object. The best way to do this is by inflating a menu resource. If you need to call any MenuItem
methods introduced in API level 11 or later, you need to call the shim for that method in MenuItemCompat
instead. Here's an example:
@Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu getMenuInflater().inflate(R.menu.main_menu, menu); // Find the share item MenuItem shareItem = menu.findItem(R.id.menu_share); // Need to use MenuItemCompat to retrieve the Action Provider mActionProvider = (ShareActionProvider) MenuItemCompat.getActionProvider(shareItem); return super.onCreateOptionsMenu(menu); }
ActionBarCompat contains it’s own ActionBar
class, and to retrieve the Action Bar attached to your activity you call getSupportActionBar()
. The API exposed by the compatibility class mirrors that of the framework ActionBar class, allowing you to alter the navigation, show/hide it, and change how it display it’s contents.
To start an action mode from your ActionBarActivity
simply call startSupportActionMode()
, providing a callback similar to how you would with the native action mode. The following example shows how to start an action mode, inflate a menu resource and react to user’s selection:
startSupportActionMode(new ActionMode.Callback() { @Override public boolean onCreateActionMode(ActionMode actionMode, Menu menu) { // Inflate our menu from a resource file actionMode.getMenuInflater().inflate(R.menu.action_mode_main, menu); // Return true so that the action mode is shown return true; } @Override public boolean onPrepareActionMode(ActionMode actionMode, Menu menu) { // As we do not need to modify the menu before displayed, we return false. return false; } @Override public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem) { // Similar to menu handling in Activity.onOptionsItemSelected() switch (menuItem.getItemId()) { case R.id.menu_remove: // Some remove functionality return true; } return false; } @Override public void onDestroyActionMode(ActionMode actionMode) { // Allows you to be notified when the action mode is dismissed } });
Similar to the Activity class, ActionBarActivity
provides two methods that you can override to be notified when an action mode is started/finished from within your Activity
(including attached Fragment
s). The methods are onSupportActionModeStarted()
and onSupportActionModeFinished()
.
Simple example
Up navigation support is built into ActionBarCompat and is similar to that provided in the Android framework in Android 4.1 and higher.
In the simplest form, you just need add a reference in each AndroidManifest
<activity>
element to its logical parent activity. This is done by adding a metadata element to the <activity>
element. You should also set the android:parentActivityName
attribute for explicit Android 4.1 support, although this is not strictly necessary for ActionBarCompat:
<activity android:name=".SampleActivity" android:parentActivityName=".SampleParentActivity"> <meta-data android:name="android.support.PARENT_ACTIVITY" android:value=".SampleParentActivity" /> </activity>
In your Activity you should then configure the home button to display for up navigation:
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
When the user now clicks on the home item, the user will navigate up to the parent Activity
set in your AndroidManifest
.
Advanced calls
There are a number of methods which can be overridden to customize what happens when a user clicks on the home item. Each of the methods below replicate/proxy similarly-named methods added in Android 4.1:
nSupportNavigateUp()
supportNavigateUpTo(Intent)
getSupportParentActivityIntent()
supportShouldUpRecreateTask(Intent)
onCreateSupportNavigateUpTaskStack(TaskStackBuilder)
and onPrepareSupportNavigateUpTaskStack(TaskStackBuilder)
When using ActionBarActivity
you should call supportRequestWindowFeature()
to request window features. For instance the following piece of code requests an overlay action bar:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Needs to be called before setting the content view supportRequestWindowFeature(Window.FEATURE_ACTION_BAR_OVERLAY); // Now set the content view setContentView(R.layout.activity_main); ... }
Similar to the native Action Bar, ActionBarCompat supports displaying progress bars on the Action Bar. First you need to request the window feature, you then display or alter the progress bars with one of the following methods, each of which mirror functionality in the similar named method from Activity
:
setSupportProgress()
setSupportProgressBarIndeterminate()
setSupportProgressBarVisibility()
setSupportProgressBarIndeterminateVisibility()
Below is an example which requests an indeterminate progress bar, and then later displays it:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Needs to be called before setting the content view supportRequestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); // Now set the content view setContentView(R.layout.activity_main); ... // When ready, show the indeterminate progress bar setSupportProgressBarIndeterminateVisibility(true); }
The Action Bar API Guide covers a lot of what is in this post and much more. Even if you have used the Action Bar API in the past, it’s definitely worth reading to see how ActionBarCompat differs from the framework.
There are also a number of sample applications using ActionBarCompat covering how to use the library in various ways. You can find them in the “Samples for SDK” package in the SDK Manager.
Check out the video below for a quick hands-on with ActionBarCompat.