I
don’t know how many hours I’ve spent struggling with Android’s theme
engine, trying to figure out — mostly by trial and error — how styles
are applied and how to properly define them (let’s be honest: the
documentation on styles in Android is a bit lacking). I thought it may
be worth sharing what I’ve found out so far, in order to save my fellow
Android developers from baldness due to ripped out hair (I sure lost
some). This is a work in progress (so is Android), and some information
may be inaccurate. If you find anything that you think is wrong or
unclear, please let me know by dropping a line in the comments section.
On another note, I assume that you have read the reference documentation on Android styles, if not, you should do that now.
What are Android styles?
A style in Android is a collection of attribute/value pairs applied
to a view, an Activity or the entire application (which means, all
Activities in your application). Styles for Activities are called
themes, but do not get confused: they are syntactically equivalent to
styles (because they are styles), they merely have a different
scope. At this point I want to clarify something upfront: Themes or
styles in Android have nothing to do with user styles known
from applications such as, say, Firefox. Android users can not change
the looks of Android applications by downloading themes from the Market
or creating their own (yet). Android themes are of interest for
developers only, think of them as syntactic sugar, a way of making your
application’s view code more DRY.
How do I define custom styles?
Let’s look into a simple theme definition (stored in res/values/styles.xml):
01 |
<? xml version = "1.0" encoding = "utf-8" ?> |
03 |
< style name = "MyTheme" parent = "android:Theme.Light" > |
04 |
< item name = "android:windowNoTitle" >true</ item > |
05 |
< item name = "android:windowBackground" >@color/translucent_blue</ item > |
06 |
< item name = "android:listViewStyle" >@style/MyListView</ item > |
09 |
< style name = "MyListView" parent = "@android:style/Widget.ListView" > |
10 |
< item name = "android:listSelector" >@drawable/list_selector</ item > |
First, we declare a new theme called MyTheme, which inherits
from another theme called Theme.Light in the android namespace
(specified by the ‘parent’ attribute). This means that all styles we do
not explicitly specify in our custom theme will be merged down from the
definition of android:Theme.Light (the curious among you may want to
download the Android source code and go to
frameworks/base/core/res/res/values to see how it is defined; all stock
styles are defined in themes.xml and styles.xml in that folder).
In our theme definition, we also set some custom styles using the
‘item’ element. We set the windowNoTitle attribute to true, so all
activities that use our theme won’t have a title bar. We also set the
window background color to a custom color called ‘translucent_blue’;
this is a resource reference (indicated by the @ symbol) to a color
definition in values/colors.xml. We could also have passed a reference
to a drawable here, or have provided a hex color value directly.
Finally, we set the default style for ListView widgets to a custom style
called MyListView. Its style definition follows the exact same
structure as the MyTheme definition, just that this time it is not a
theme, but simply a style that can be applied to ListView objects. It
inherits from the default styles for ListView but replaces the default
selector image with a custom drawable.
There are two important things to understand here. First, those two
style definitions are completely independent of each other (well, apart
from the fact that we reference MyListView in MyTheme of course). This
means, that if I remove the reference to MyListView from MyTheme, I can
still use that style by applying it manually to a ListView declaration
in a layout file. Think of styles simply as a bunch of attributes and
values you would have otherwise typed directly into your view
definition, so instead of writing
1 |
< ListView android:listSelector = "@drawable/list_selector" /> |
we write
1 |
< ListView style = "@style/MyListView" /> |
or better yet, we let this style be applied automatically to all
ListView widgets by setting it in a theme definition (as seen above).
(Note the missing ‘android:’ namespace reference here; this is
intentional, the ‘style’ attribute is not defined in the ‘android’
namespace. You will get an error if you try to set a style using
‘android:style’, there is no such attribute.)
That way we never have to touch the ListView definition anymore; we
can do all its styling from a single point, the style sheet. This helps
in leaving the code for defining structure in your layouts, while leaving the code for defining appearance in your style sheets — a good example for separation of concerns.
More importantly, your style definitions can be reused, which is
particularly useful if styles are shared between several views.
The other important thing to understand is that styles do not have
any type semantics. When we create a style called MyListView which
inherits from Widget.ListView, then the intuition is that we are
creating a style that is only supposed to apply to ListView objects.
That’s not entirely correct though. There is no mechanism that will
check whether we indeed apply that style to a ListView or even prevent
us from applying it to an entirely different view type. It just so
happens that Widget.ListView defines some attributes that only make
sense when being applied to a ListView (such as listDivider), this
doesn’t stop us, however, from creating a style that totally makes sense
for any widget type (maybe because it only uses attributes defined in
the View class, the parent class of all views). The bottom line is that
you have to figure out yourself whether a style you define makes sense
when you apply it to a view. Android will not do any sanity checks for
you. In the worst case, the targeted view will expose odd behavior when
being rendered, but more probably nothing will happen at all.
So what can I style, and from what can I inherit?
To get going with styles, it’s of course important to know two things:
- what’s there to be styled, and
- what styles are already there to inherit from
The easy answer is: Anything in android.R.styleable can be used inside a style body as a subject for styling (using the item tag), while anything in android.R.style
can be used as parent styles to inherit from. So, to stick with the
ListView example style, the android:listSelector style item can be found
in android.R.styleable, while android:style/Widget.ListView is defined
in android.R.style. Those two files are therefore what you want to turn
your attention to first when defining custom styles. You should
generally always inherit all default style items first, and then
overwrite them one by one with your custom items.
That’s basically already it, a simple yet powerful style framework if used correctly!
Useful Tidbits
Text Appearance
Did you find yourself defining text appearance like font size or text
color over and over again in your layouts? Don’t do that, use text appearances
instead. Text appearances are styles, too, and Android already defines
some for you which you can override (they are, of course, defined in
R.style). This helps tremendously in keeping your view code DRY, and
encourages you to get some structure into your different font styles
used throughout your app by grouping them into styles you can reference
and reuse. If, for example, you want to change the default text
appearance in your app, simply define a custom text appearance in your
stylesheet and set it as the default in your theme:
01 |
<? xml version = "1.0" encoding = "utf-8" ?> |
03 |
< style name = "MyTheme" parent = "android:Theme.Light" > |
04 |
< item name = "android:textAppearance" >@style/MyDefaultTextAppearance</ item > |
07 |
< style name = "MyDefaultTextAppearance" parent = "@android:style/TextAppearance" > |
08 |
< item name = "android:textSize" >12sp</ item > |
09 |
< item name = "android:textColor" >#333</ item > |
10 |
< item name = "android:textStyle" >bold</ item > |
Make yourself familiar with all the text appearances Android already
defines, and customize them for your application as you see fit.
Colors
Avoid setting color values directly in layouts or styles. Define them
in a color resource file instead, and only use references to those
color values from your layouts and styles. This helps in isolating them
from the rest of your view code and makes your styles less brittle if
you should decide to change a color afterwards. For simple, non-stateful
colors, you can simply create a colors.xml file in res/values:
1 |
<? xml version = "1.0" encoding = "UTF-8" ?> |
3 |
< color name = "default_text_color" >#FFEAEAEA</ color > |
Often, however, colors can be more complex. If, for instance, you use
a custom list selector image, it may happen that list text is readable
when not selected, but becomes difficult or impossible to read when the
custom selector is drawn (e.g. because the coloring is too similar). In
that case, you need a ColorStateList,
which you can use to automatically change colors for the view it’s
applied to, based on that view’s state. Start by creating a new file,
e.g. ‘stateful_text_color.xml’ in your res/colors directory. The
resource root is ‘selector’, which is what you always use when creating
resources that change with a view’s state:
2 |
< item android:state_enabled = "false" android:color = "#333" /> |
3 |
< item android:state_window_focused = "false" android:color = "#CCC" /> |
4 |
< item android:state_pressed = "true" android:color = "#FFF" /> |
5 |
< item android:state_selected = "true" android:color = "#FFF" /> |
6 |
< item android:color = "@color/default_text_color" /> |
You get the idea. You can set this stateful color resource like any other color, it’s simply defined in a different way.
Also keep in mind that there are already color definitions shipped
with Android. The most useful of them is probably
android:color/transparent, which corresponds to the color #00000000. The
first 8 bit, i.e. the first two hex digits, represent the alpha channel
which is used to define opacity. A value of zero means zero opacity, or
100% transparency; the remaining color bits are therefore of no
relevance, because they will be invisible. You can then turn the
background of your views transparent by doing something like:
1 |
< style name = "MyListView" parent = "@android:style/Widget.ListView" > |
2 |
< item name = "android:background" >@android:color/transparent</ item > |
That’s it for now. If I find out more about styles in Android, I’ll
update this post accordingly. Feel free to contribute in the comments
section, too.