Android Themes: an Adventure

2010/10/25

I am the author of a simple Android app widget , “Uptime Widget” that shows the uptime, and max uptime of the user’s device. As widgets are rendered on the home screen their readability greatly varies depending on the user’s wall paper. I got a lot of request to provide “lighter” or “darker” versions of widget. This prompted me to investigate Android’s theming abilities.

My first attempt didn’t use themes or styles at all. I integrated the simple color picker dialog from the Android API demos, with the intention of having an app widget configuration activity that allowed the user to pick their background and text color, at the least. I found at the hard way the RemoteViews, the view class used to build app widgets doesn’t allow programmatic setting of the background color.  Back to the drawing board.

My next attempt was to use Android’s theme / style facilities in full. Essentially, define several themes and allow the user to pick one in a configuration activity. This page has an excellent example and sample code. I was quite frustrated before finding this, as there aren’t really any good examples of how to make dynamic references to styles, versus statically declaring their use in the layout. That’s critical if you are to have >1 theme for your application. Unfortunately, you can’t use this method for app widgets. I don’t understand why at this point, another limitation in RemoteViews. I filed an issue against the AOSP. I won’t hold my breath.

The only option left is to statically declare style usage in the app widget’s layout. To get multiple, dynamic themes, the app widget must build the RemoteViews object based on a dynamically picked layout ID. That means having one layout per theme (and in my case, additionally, per orientation). Uptime Widget has 8 very similar layouts. A terrible solution by all regards. Modifying the UI or adding a theme is a tedious error-prone endeavor. But it works. You can take a look at the source over at the Google code project if you are interested.

All in all, I found Android style / theme mechanism lacking. First. the method of referencing a theme is awkward. You must first declare a reference attribute,

<attr name="Caption" format="reference"/>

then, create various styles that map to the reference,

<style name="Dark.Caption">
  <item name="android:textColor">@android:color/black</item>
</style>
<style name="Light.Caption">
  <item name="android:textColor">@android:color/white</item>
</style>

Now, map that reference to real styles within a theme,

<style name="DarkTheme">
  <item name="Caption" value="@style/Dark.Caption"/>
</style>
<style name="LightTheme">
  <item name="Caption" value="@style/Light.Caption"/>
</style>

Finally, use the attribute reference in your layout,

<TextView style="?Caption" .../>

and set the theme using the android:theme attribute to either “DarkTheme” or “LightTheme” at either the application or activity level in your manifest. What’s missing? Selectors. For example, if you apply a theme with android:textColor=”…”, it applies to every element in the layout. The only way to get around this is to call out the specific style (or reference to a style, as shown above) in each view element. We need to be able to select on the element type and  ID like,

<item name="android:textColor" selector="@+id/topLayout/*.TextView">
  @android:color/black
</item>

This styles only TextViews under the element with the topLayout. There would need to be some advanced syntax, for example, to select elements recursively. CSS is not simple, and it would not be simple to mimic it either, but without this Android styles are sorely lacking.