Nesting Android Themes

Android Themes are not very intuitive, and I bet I’m not the only one that has spent a whole evening scratching my head figuring out why something that, on the surface, should be working is not. The documentation is scarce and confusing. Especially regarding style and theme inheritance.

There are two ways a Theme can descend from another one. And the same applies to styles. The first one, and most straight forward, is to define the parent attribute in the XML description.

<style name”AppTheme” parent=”BaseTheme”></style>

The other one, useful as a horrible interview question, is using namespacing.

<style name”BaseTheme.Dialog”></style>

Doing so will inherit all the attributes from BaseTheme into this new one. One thing to note here is that this inheritance only works with project defined styles. If we want to inherit from a system one like TextAppearance we still have to use the parent attribute. AppCompat styles are part of the project’s resources, so they can be used as if they were declared locally.

Nesting for compatibility

One of the uses of Theme inheritance is when we have screens that require the same base attributes but differ in a few of them.

Another, more interesting, use is to support new versions of Android and their latest juicy features. For instance, if we want to set the colour of the status bar to match our app’s branding we can do it such as:

<!-- values/themes.xml -->
<style name=”BaseTheme” parent=”Theme.AppCompat.Light.NoActionBar”>
<item name=”colorPrimary”>@color/colorPrimary</item>
<item name=”colorPrimaryDark”>@color/colorPrimaryDark</item>
<item name=”colorAccent”>@color/colorAccent</item>
</style>
<style name=”AppTheme” parent=”BaseTheme”>
<!-— overriden in other configurations →
</style>
<!-- values-21/themes.xml -->
<style name=”AppTheme” parent=”BaseTheme”>
<item name=”android:statusBarColor”>@color/colorPrimaryDark</item>
</style>

Applying themes like this means that, for old platforms, the status bar will remain with the standard and boring colour, but new devices can match your branding and make the experience more immersive.

Additionally, this means we have a single source of our required theme attributes, shared between them all. The benefit of such set up goes along with the obvious upsides of inheritance: reusability and maintenance ease. Next time we want to change a theme, we don’t have to chase around all the places where we defined them.

Moar nesting

If we use a light colour (white for instance), we will be hiding the notification icons.

In the previous example, we use colorPrimaryDark to make sure this doesn’t happen. Android added a solution in Marshmallow (API 23) that allows us to change the icons in the status bar to a darker shade, so it works better with lighter tones.

Now, the problem we find is that we already have two levels of nesting in our theme. It’s common at this crossroad for some people to just copy the values on the different versions. Do not do that; it’ll haunt you sooner or later.

Instead, we can do something similar to what the AppCompat library does. We’ll leave the default themes as they were, but we have to declare the other two a bit differently.

<! — values-21/themes.xml -->
<style name=”BaseThemeV21" parent=”BaseTheme”>
<item name=”android:statusBarColor”>@color/colorPrimaryDark</item>
</style>
<style name=”AppTheme” parent=”BaseThemeV21">
<!-— overrides base AppTheme -->
</style>
<!-- values-23/themes.xml -->
<style name=”BaseThemeV23" parent=”BaseThemeV21">
<item name=”android:statusBarColor”>@color/colorPrimary</item>
<item name=”android:windowLightStatusBar”>true</item>
</style>
<style name=”AppTheme” parent=”BaseThemeV23">
<!— overrides base AppTheme -->
</style>

In this instance, statusBarColor is overridden in the v23 theme, but if we had the old model and we just overrode the AppTheme with our local BaseTheme, it would lose all the properties we set in v21.

Keep developing in style!