Chapter 27

Using the Action Bar

One of the easiest ways to make your application blend in better with the latest and greatest Android UI is to enable the action bar, introduced in Chapter 26. What makes it “easy” is that most of the basic functionality of the action bar is backward compatible—the Android 4.0 settings will not cause the application to crash on earlier versions of Android.

The sample project shown in this chapter is Menus/ActionBar, which extends the Menus/Inflation project shown in a previous chapter.

Enabling the Action Bar

By default, your Android application will not use the action bar. In fact, it will not even be displayed on the screen. If you want the action bar to appear on the screen, you need to include android:targetSdkVersion="11" or later in your <uses-sdk> element in the manifest, such as the manifest for the Menus/ActionBar project:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.commonsware.android.inflation">
    <application android:label="@string/app_name"
                android:icon="@drawable/cw"
                android:hardwareAccelerated="true">
        <activity android:name=".InflationDemo" android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
    </application>
    <uses-sdk android:minSdkVersion="4" android:targetSdkVersion="11" />
    <supports-screens android:xlargeScreens="true"
                    android:largeScreens="true"
                    android:normalScreens="true"
                    android:smallScreens="true"
                    android:anyDensity="true"/>
</manifest>

This will cause your options menu to appear in the upper-right corner of the screen, under a menu icon in the action bar, as shown in Chapter 26. Also, your activity's icon will appear in the upper-left corner, with your activity's name (from the android:label attribute in the manifest) alongside of it.

While this gives you the basic contemporary look and feel—including the Ice Cream Sandwich–themed widgets—it does not really change the user experience all that much.

Promoting Menu Items to the Action Bar

The next step for integrating with the action bar is to promote certain options menu items from being part of the options menu to being always visible on the action bar itself. This makes them easier to find and saves the user a tap when the time comes to use them.

To do this, in your menu XML resource, you can add the android:showAsAction attribute to an <item> element. A value of ifRoom means that the menu item will appear in the action bar if there is space for it, while a value of always means that the menu item will always be put in the action bar. All else being equal, ifRoom is the better choice, as it will adapt better to smaller screens, once the Honeycomb UI moves onto phones. You can also combine this with the withText value (e.g., ifRoom|withText) to make the title of the menu item appear adjacent to the item's icon (otherwise, only the icon appears in the action bar).

For example, the Menus/ActionBar project's options.xml menu resource has android:showAsAction on the first two menu items:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
  <item android:id="@+id/add"
    android:title="Add"
    android:icon="@drawable/ic_menu_add"
    android:actionLayout="@layout/add"
    android:showAsAction="ifRoom"/>
  <item android:id="@+id/reset"
    android:title="Reset"
    android:icon="@drawable/ic_menu_refresh"
    android:showAsAction="ifRoom|withText"/>
  <item android:id="@+id/about"
    android:title="About"
    android:icon="@drawable/ic_menu_info_details" />
</menu>

The second menu item, Reset—for resetting the contents of the list—is a normal “with text” action bar button. The first menu item, Add, does something a bit different, which we will examine later in this chapter. The fact that the third menu item, About, does not have android:showAsAction means that it will remain in the menu, even if there is room in the action bar.

Note that the Java code does not change—onCreateOptionsMenu() and onOptionsItemSelected() for our InflationDemo activity do not need to be adjusted because menu items are promoted into the action bar via the menu XML resource alone.

Responding to the Logo

The activity icon in the upper-left corner of the screen is tappable. If the user taps it, it triggers onOptionsItemSelected()…but not for one of the options menu items you may have defined yourself. Rather, the magic value of android.R.id.home is used. In the Menus/ActionBar project, we wire it to the same code that is invoked if the user chooses the About options menu item—displaying a Toast:

@Override
public boolean onOptionsItemSelected(MenuItem item) {
  switch (item.getItemId()) {
    case R.id.add:
      add();
      return(true);

    case R.id.reset:
      initAdapter();
      return(true);

    case R.id.about:
    case android.R.id.home:
      Toast
        .makeText(this,
                  "Action Bar Sample App",
                  Toast.LENGTH_LONG)
        .show();
      return(true);
  }

  return(super.onOptionsItemSelected(item));
}

In a project with multiple activities, though, the expectation is that tapping the logo will take you to the “home” activity for the application, whatever that might mean.

Adding Custom Views to the Action Bar

You can do more with the action bar than simply convert options menu items into what amount to toolbar buttons. You can add your own custom UI to the action bar. In the case of Menus/ActionBar, we'll replace the Add menu choice and resulting dialog box with an Add field right in the action bar itself.

This, however, is a bit tricky to implement, as described next.

Defining the Layout

To put something custom in the action bar, we need to define what the “something custom” is, in the form of a layout XML file. Fortunately, we already have a layout XML file for adding a word to the list—it is the one that the Menus/Inflation sample wrapped in a custom AlertDialog for when the Add options menu item was tapped. That original layout looked like this:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    >
  <TextView
      android:text="Word:"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      />
  <EditText
      android:id="@+id/title"
      android:layout_width="fill_parent"
      android:layout_height="wrap_content"
      android:layout_marginLeft="4dip"
      />
</LinearLayout>

We need to make some minor adjustments to this layout to use it for the 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="fill_parent"
    android:layout_height="wrap_content"
    >
  <TextView
      android:text="Word:"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:textAppearance="@android:style/TextAppearance.Medium"
      />
  <EditText
      android:id="@+id/title"
      android:layout_width="fill_parent"
      android:layout_height="wrap_content"
      android:layout_marginLeft="4dip"
      android:width="160sp"
      android:inputType="text"
      android:imeActionId="1337"
      android:imeOptions="actionDone"
      />
</LinearLayout>

Specifically, we made these minor adjustments:

  • We added an android:textAppearance attribute to the TextView representing our Add caption. The android:textAppearance attribute allows us to define the font type, size, color, and weight (e.g., bold) in one shot. We specifically used a magic value of @android:style/TextAppearance.Medium so that the caption matches the styling of the Reset label on the other menu item we promoted to the action bar.
  • We specified android:width="160sp" for the EditText widget, because android:layout_width="fill_parent" is ignored in the action bar—otherwise, we would take up the rest of the bar.
  • We specified android:inputType="text" on the EditText widget, which, among other things, restricts us to a single line of text.
  • We  android:imeActionId and android:imeOptions on the EditText widget to control the action button of the soft keyboard, so we get control when the user presses the Enter key on the soft keyboard.

Putting the Layout in the Menu

Next, we need to teach Android to use this layout for our Add options menu item if we are running on up-to-date releases of Android, such as Ice Cream Sandwich or Honeycomb. To do this, we use the android:actionLayout attribute on our <item> element, referencing our layout resource (@layout/add), as was shown earlier in this chapter. This attribute will be ignored on earlier versions of Android, so it is safe to use.

If we did nothing else, we would get the desired UI, shown in Figure 27–1.

images

Figure 27–1. The Menus/ActionBar sample application

However, while the user could type something in, we have no way to find out what they type in, when they are done, and so forth.

Getting Control of User Input

Given our soft keyboard settings we put on the EditText widget, we can arrange to find out when the user presses the Enter key either on the soft keyboard or on a hardware keyboard. To do that, though, we need to get our hands on the EditText widget itself. You might think it is added when the UI is inflated in onCreate()…but you would be mistaken.

With an action bar, onCreateOptionsMenu() is called after onCreate() as part of setting up the UI. On classic versions of Android, onCreateOptionsMenu() would not be called until the user pressed the Menu button. But, since some of the options menu items might be promoted into the action bar, Android calls onCreateOptionsMenu() automatically now. The EditText will exist after we inflate our options.xml menu resource.

However, the best way to get the EditText is not to use findViewById() on the activity. Rather, we should call getActionView() on the MenuItem associated with our Add option. This will return the root of the view hierarchy inflated from the layout resource we defined in the android:actionLayout attribute in the menu resource. In this case, that is the LinearLayout from res/layout/add.xml, so we need to call findViewById() on it to get the EditText:

@Override
public boolean onCreateOptionsMenu(Menu menu) {
  new MenuInflater(this).inflate(R.menu.option, menu);

  EditText add=(EditText)menu
                         .findItem(R.id.add)
                         .getActionView()
                         .findViewById(R.id.title);

  add.setOnEditorActionListener(onSearch);

  return(super.onCreateOptionsMenu(menu));
}

Then, we can call setOnEditorActionListener() on the EditText, to register an OnEditorActionListener object that will get control when the user presses Enter on the hard or soft keyboard:

private TextView.OnEditorActionListener onSearch=
  new TextView.OnEditorActionListener() {
  public boolean onEditorAction(TextView v, int actionId,
                               KeyEvent event) {
    if (event==null || event.getAction()==KeyEvent.ACTION_UP) {
      addWord(v);

      InputMethodManager imm=(InputMethodManager)getSystemService(INPUT_METHOD_SERVICE);

      imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
    }

    return(true);
  }
};

That in turn calls an addWord() method, supplying the EditText, which adds the word to the list via the ArrayAdapter:

private void addWord(TextView title) {
  ArrayAdapter<String> adapter=(ArrayAdapter<String>)getListAdapter();

  adapter.add(title.getText().toString());
}

That same addWord() method can also be used from the add() method that displays the AlertDialog, even though that will not be used on a tablet, since the Add menu choice no longer exists as a menu choice:

private void add() {
  final View addView=getLayoutInflater().inflate(R.layout.add, null);

  new AlertDialog.Builder(this)
    .setTitle("Add a Word")
    .setView(addView)
    .setPositiveButton("OK",
                       new DialogInterface.OnClickListener() {
      public void onClick(DialogInterface dialog,
                           int whichButton) {
        addWord((TextView)addView.findViewById(R.id.title));
      }
    })
    .setNegativeButton("Cancel", null)
    .show();
}

The net result is that when the user types something in the Add field and presses the Enter key, the word is added to the bottom of the list. This saves some taps over the traditional phone UI, as the user does not have to open the options menu, does not have to tap the options menu item, and does not have to tap a button on the dialog box.

Note that our OnEditorActionListener does something more than simply add the word to the list: it hides the soft keyboard. It does this using the InputMethodManager, as was seen in a previous chapter.

Don't Forget the Phones!

With the exception of the custom view feature described in the preceding section, everything shown in this chapter regarding the action bar is automatically backward compatible. The code and resources that work on Ice Cream Sandwich–flavored versions of Android will work on classic versions of Android unmodified.

If, however, you want to use the custom view feature, you have a problem—the getActionView() method was new to API Level 11 and will be unavailable on older versions of Android. This means you will need to compile for at least API Level 11 (e.g., set your Eclipse target or Ant default.properties to reference android-11) or higher, and you will need to take steps to avoid calling getActionView() on older devices. We will explore how to pull off this feat in a later chapter.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset