Customizing the Action Bar in Android

One of the nice things about Windows Phone is the application bar and the consistency that it can bring for buttons.  More and more we are seeing application utilize the application bar for their screen actions; this is good because it reduces confusion for the user when knowing what actions they can take.

With Honeycomb Android introduced the Action Bar to replace the context menu.  The advantage that this gave was, like Windows Phone, a consistent location for screen actions.  The problem was that the options were not visible by default and required the user to “open” the menu, usually through a hard button.  The new action bar combined the title of the screen with allowable options with an expandable menu invoked through a soft button.

But I mentioned consistency with applications and the one thing that I see a lot with Android apps is inconsistency.  Despite there being Android Design Guidelines you still see a lot of free form with design, in particular with button location.  But one pattern that Google seems to be advocating is, like the application bar in Windows Phone, an application bar look in Android, demonstrated here in the Android Calendar application:

Screenshot_2013-10-13-17-56-35

I decided I wanted this UI design in my application for an edit/create screen, however it would appear there is no built in simple way to do this, instead you need to create a Custom View for your Action Bar.  I spent some time looking online and really didnt find anything super concrete so I decided to check the Android Calendar source (yeah!! open source).  The source is available here: https://github.com/android/platform_packages_apps_calendar

Before you begin:

Understand that the Action Bar is an Android 3.0+ concept and this example will assume that.  If you are building an application to target pre-Honeycomb releases (which is still the second largest majority) be mindful that this approach wont work.

1) Theme your activity

The first thing is to set some simple display properties on your activity.  The best way to do this is to use a style defined in the AndroidManifest.xml

<activity android:name="MyActivity" android:label="@string/app_name"
          android:theme="@style/ActivityWithActionBar" />

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

Here is the style which shows the Display Options we are affecting:

    <style name="ActivityWithActionBar" parent="android:Theme.Holo.Light">
        <item name="android:windowNoTitle">false</item>
        <item name="android:windowActionBar">true</item>
        <item name="android:windowBackground">@color/background_color</item>
        <item name="android:actionBarStyle">@style/ActionBarStyle</item>
    </style>

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

Now, we need a view for the custom action bar view

2) Create your view

Literally what you will be doing is creating a layout which is then inflated and passed into the action bar.  Here is the XML for the layout of the custom action bar:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="horizontal"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:divider="?android:attr/dividerVertical"
              android:dividerPadding="12dp"
              android:showDividers="middle">

    <LinearLayout android:id="@+id/action_cancel"
                  style="@style/CustomActionButton">

        <ImageView android:src="@drawable/ic_menu_cancel_holo_light" style="@style/ActionButtonImage" />
        <TextView android:text="DISCARD" style="@style/ActionButtonText" />

    </LinearLayout>

    <LinearLayout android:id="@+id/action_done" style="@style/CustomActionButton">

        <ImageView android:src="@drawable/ic_menu_done_holo_light" style="@style/ActionButtonImage" />
        <TextView android:text="DONE" style="@style/ActionButtonText" />

    </LinearLayout>

</LinearLayout>

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

We have a horizontal linear layout encapsulating two linear layouts which will masquerade as our buttons.  The styles used in this example, all are taken, verbatim from the Android Calendar source, see them here: http://pastebin.com/3xzZcJpc

You will notice that this example uses two drawables for the ‘X’ and the checkmark in the button.  These coming directly from the source, download them here:

So, we now have our view XML for the custom action bar.

3) Set the action bar

As I mentioned earlier, there is no built in component that allows for this in a consistent fashion.  Instead, using a reference to the activities action bar with the appropriate display options, an inflated view is set as the action bar’s view content.  Here is the code that does this:

        View customActionBar = getLayoutInflater().inflate(R.layout.custom_action_bar,
                new LinearLayout(this), false);
        getActionBar().setCustomView(customActionBar);

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

In this example, the name of our custom action bar view is the layout custom_action_bar.  We indicate that our action bar will have a custom view through the setCustomView method call.

One thing which should be pointed out is that when doing this you are no longer utilizing the Option Menu which means when the user presses the button in your custom view it will not send an item action.  Instead you need to wire these up manually.  Its quite easy as the following code snippet shows:

        View cancelActionView = customActionBar.findViewById(R.id.action_cancel);
        View doneActionView = customActionBar.findViewById(R.id.action_done);

        cancelActionView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //To change body of implemented methods use File | Settings | File Templates.
            }
        });

        doneActionView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //To change body of implemented methods use File | Settings | File Templates.
            }
        });

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

For your reference, the majority of how I figured this came from the EditEventFragment in the Calendar source: http://bit.ly/EditEventFragment (617)

So as I stated earlier, Apple has shown us that consistency in a platform is important when you start talking about usability.  Knowing where something is regardless of the app brings an instant familiarity for users.  Windows 8 is built on this methodology through the use of charms for commonly used features.  I think, as I see this in most Google apps and other non-Google apps the inclination is that this should be a established pattern especially edit/create screens.

Hope this helps

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s