Since the release of Android 4.0, we have seen an increase in applications that follow the new Android design guidelines. The guide describes the Android holo theme that provides a cohesive user experience that is both modern and unique. At this years Google IO talk "Android Design for Success", we got a handy outline of how important these guidelines can be:
For this post, I'll assume you have the ground covered in the "Successful design" segment. So let's head out into app nirvana. More specifically I'll describe some useful patterns when applying heavier branding to your application.
The simplest way to declare the visual appearance in Android is to set properties directly on the View object. Since this isn't very convenient when styling an entire application, we can create styles that bundle properties of view. A style can than be set onto a view. But be aware that a style can only be set in xml. That means that we can only set a style once because the style properties are evaluated when the View is created/inflated. Here is an example of how to set properties for a TextView in the res/values/styles.xml file.
<pre lang="xml"> <style name="CustomText" parent="@android:style/TextAppearance.Medium"> <item name="android:textSize">20sp</item> <item name="android:textColor">#008</item> </style> </pre>
To reference this style in our layout.xml files we use
@style/CustomText. The style declaration references a parent style called
@android:style/TextAppearance.Medium. Since the style reference begins with
android:namespace, we know that it is one of the default styles shipped with the Android platform.
Creating themes to bundle styles
Let's assume we want to change the text size and color on all of our TextViews and don't want to set it explicitly on every TextView. This is a very common scenario and luckily Android offers a pretty powerful mechanism via themes. In essence, a theme is a style resource itself that uses "keys" to point to concrete styles. A View object greps the concrete style to apply based on the key. Here is a simple example that would be declared in the
<pre lang="xml"> <style name="Theme.MyApp" parent="@android:style/Theme.Holo"> <item name="android:textAppearance">@style/CustomText</item> </style> </pre>
As mentioned above, a theme is style resource itself so we declare a
Theme.Appthat inherits from the dark holo theme provided by the Android platform. We also assign our CustomText style to the attribute
android:textAppearance. An attribute is a predefined "variable" that can hold references to other resource elements. In fact, it is also possible to create custom attributes in the
Now, here comes the interesting part. Our theme
Theme.MyApp can not only be applied by setting it on an Activity in the
AndroidManifest.xml, it can also be set during runtime in code. You would have to restart the current Activity (or use a
ContextWrapper to theme parts of your UI) but it makes theming much more dynamic.
How to find out what attributes to set on widgets
The most common problem with theming is to find out what attributes apply to which View. The only reliable way to find all the corner cases is to have a look at the source code of the View we want to theme. The following snippet shows parts of the TextView class in Android api level 16 (Jelly Bean). The snippet is executed in the constructor (code passages are omitted).
<pre lang="java"> final Resources.Theme theme = context.getTheme(); TypedArray a = theme.obtainStyledAttributes(attrs, com.android.internal.R.styleable.TextViewAppearance, defStyle, 0); int ap = a.getResourceId(com.android.internal.R.styleable.TextViewAppearance_textAppearance, -1); TypedArray appearance = theme.obtainStyledAttributes(ap, com.android.internal.R.styleable.TextAppearance); textColor = appearance.getColorStateList(attr); </pre>
The snippet shows how the text color is extracted from the current theme using the attribute
TextAppearance. By reading through this initialization code we can usually derive what kinds of elements can be configured.
We have to be aware though, that different Android versions apply different attributes. While higher Android versions usually support older attributes, newer attributes are obviously not interpreted by older Android versions. I hope we will get a Lint check soon when referencing theme attributes that are introduced on newer Android versions.
Theming has gotten much more powerful with Android 4.0. Ninety-eight percent of the attributes that can be set via code, can now be set via styles and are therefore themeable. In addition to the greater visual flexibility when using a custom theme, you also reduce clutter in your xml layouts and in your java code which can now be completely devoted to the app's logic.