Home > android, ui > Custom themes for Honeycomb and backwards compatibility

Custom themes for Honeycomb and backwards compatibility

Here is a couple of things I learned while recenly updating my apps to have the native look on tablets running Android Honeycomb (3.*).

My app lets the user select a UI theme (there are two: light and dark), so I had something like this in my res/values/styles.xml:

<declare-styleable name="WifiTheme">
	<attr name="radar_text" format="color" />
	<attr name="radar_alpha" format="color" />
</declare-styleable>

<style name="WifiTheme.Light" parent="@android:style/Theme.Light">
	<item name="radar_text">#FF202020</item>
	<item name="radar_alpha">#80000000</item>
</style>

<style name="WifiTheme.Dark" parent="@android:style/Theme">
	<item name="radar_text">#FFFFFFFF</item>
	<item name="radar_alpha">#A0000000</item>
</style>

I would then apply one of these themes, from inside onCreate, to my activities.

Now, Honeycomb has new “Holographic” UI themes that look different from 2.* themes, and users expect an application running on a tablet to use those. The two new themes are @android:style/Theme.Holo.Light and @android:style/Theme.Holo.

It’s possible to duplicate the XML snippet above in res/values-11/styles.xml, replacing the parent themes, but that would mean duplicating all the values for those themes. The snippet above has just two extra attributes, but there could be many more of them.

Ok, so here is a trick I came up with. Just like with program code, what we need to get out of a tight spot is an extra level of abstraction.

First, I defined the following themes in res/values/theme_compat.xml:

<style name="ThemeCompat">
</style>

<style
	name="ThemeCompat.Dark"
	parent="@android:style/Theme">
</style>

<style
	name="ThemeCompat.Light"
	parent="@android:style/Theme.Light">
</style>

The first one is just to keep the Android style hierarchy system happy, the other two are where the real work is. You can think of them as defining a base class (in programming terms) for my custom themes. My custom theme declarations are now derived from the themes shown above, and look like this:

<style name="WifiTheme.Light" parent="@style/ThemeCompat.Light">
	<item name="radar_text">#FF202020</item>
	<item name="radar_alpha">#80000000</item>
</style>

<style name="WifiTheme.Dark" parent="@style/ThemeCompat.Dark">
	<item name="radar_text">#FFFFFFFF</item>
	<item name="radar_alpha">#A0000000</item>
</style>

And for when the application runs on Android 3.*, I have the following declarations in res/values-11/theme_compat.xml:

<style
	name="ThemeCompat.Dark"
	parent="@android:style/Theme.Holo">
</style>

<style
	name="ThemeCompat.Light"
	parent="@android:style/Theme.Holo.Light">
</style>

What this does is replaces the “base class” of my custom themes with those native to Honeycomb. Now my custom theme declarations don’t need to be duplicated, and are automatically derived at runtime from the most appropriate system themes.

Here is another thing. AlertDialogs with custom content views.

AlertDialogs can be sometimes tricky, because they have their own styling and can even impose it on their content. But I feel that using them in place of regular dialogs makes an application’s UI better. The light panel behind the buttons, the title bar with the icon, the separator line below that – those may seem insignificant little things, but I feel they really make an app look better. Some manufacturers (Samsung, Sony Ericsson, perhaps others) make changes to stock Andorid UI to add their own color and graphics accents, and so it becomes even more important to use AlertDialog to better blend in with the device’s UI.

And this is another thing where Honeycomb is different from previous Android versions. Prior to 3.0, AlertDialogs always use a dark background (unless overriden for lists), even if the parent activity’s theme is light. Starting with 3.0, alert dialogs have the same overall light/dark color scheme as the base activity.

If your alerts use custom content views, it becomes tricky, because those views need to be inflated using an appropriate Context, so they match the dialog’s color scheme.

It’s really easy to end up with white text on white background, unless you use the following method, new with API level 11: AlertDialogBuilder.getContext().

Since this method doesn’t exist prior to API level 11, it’s possible to use Java reflection to stay compatible with older Android versions. Turns out there is another way: by subclassing AlertDialog, and overriding onCreate(), it’s possible to call getContext() for the AlertDialog itself. That method exists since API level 1, and returns a correctly themed Context for both 3.* and prior versions of Android. A further shortcut is to call getLayoutInflater(), which also exists since API level 1.

With this in mind, here is a simple alert dialog:

public class DlgAbout extends AlertDialog {

	public DlgAbout(Context context) {
		super(context);
	}

	@Override
	protected void onCreate(Bundle savedInstanceState) {

		setTitle(R.string.about_title);
		setCancelable(true);

		LayoutInflater inflater = getLayoutInflater();

		View contentGroup = inflater.inflate(R.layout.about_content, null);
		setView(contentGroup);

		super.onCreate(savedInstanceState);
	}
}

The dialog’s layout can use views without any specific color customizations:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
	xmlns:android="http://schemas.android.com/apk/res/android"
	android:orientation="vertical"
	android:layout_width="wrap_content"
	android:layout_height="wrap_content"

.....

<!-- Note the absence of android:textColor, etc -->
		<TextView
			android:layout_width="wrap_content"
			android:layout_height="wrap_content"
			android:text="@string/about_1"
			android:gravity="center_horizontal" />
......

The code to use this dialog is below. Not really that much different from using AlertDialog.Builder, and is actually somewhat better because the code for the dialog is placed in its own class (above).

@Override
protected Dialog onCreateDialog(int id) {
	if (id == DIALOG_ID_ABOUT) {
		/*
		 * About the app
		 */
		return new DlgAbout(this);
	}
.........
}
Advertisements
Categories: android, ui
  1. Pavan Deshpande
    May 23, 2013 at 9:00 pm

    very nice tutorial you can also check the styling of alert boxes in android
    alert box design

  2. kp
    October 21, 2011 at 2:19 pm

    I’m just starting to address themes, so thanks for this – it’s very helpful. Do you know if the “values-11” will take care of later versions of Android like 3.2 or 4.0?

    • kp
      October 21, 2011 at 3:11 pm

      never mind, I see in your article you said 3.x, so that was a dumb question. thanks again.

      • October 21, 2011 at 5:02 pm

        values-11 will be picked up by Android 3.0 (API level 11) and later, including 4.*, 5.*, and so on.

        And since 4.0 (Ice Cream Sandwich) uses the same holographic themes as the 3.* line, well, it should just work…

      • kp
        October 21, 2011 at 5:19 pm

        yes, thanks, I figured that out after re-reading things and setting my Android version in Eclipse to 4.0. I did have to name it “values-v11” to keep Eclipse happy as well.

  1. No trackbacks yet.

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