Defining and Using Styles

As noted in an earlier chapter, Android offers styles and themes, filling the same sort of role that CSS does in Web development. In that earlier chapter, we covered the basic roles of styles and themes, plus introduced the three classic theme families:

In this chapter, we will take a slightly “deeper dive” into styles and themes, exploring how you can create your own and apply them to your app’s UI.

Styles: DIY DRY

The purpose of styles is to encapsulate a set of attributes that you intend to use repeatedly, conditionally, or otherwise wish to keep separate from your layouts proper. The primary use case is “don’t repeat yourself” (DRY) — if you have a bunch of widgets that look the same, use a style to use a single definition for “look the same”, rather than copying the look from widget to widget.

And that paragraph will make a bit more sense if we look at an example, specifically the Styles/NowStyled sample project. This is a trivial project, with a full-screen button that shows the date and time of when the activity was launched or when the button was pushed. This time, though, we want to change the way the text on the face of the button appears, and we will do so using a style.

The res/layout/main.xml file in this project has a style attribute on the Button:

<?xml version="1.0" encoding="utf-8"?>
<Button xmlns:android="http://schemas.android.com/apk/res/android"
  android:id="@+id/button"
  android:text=""
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  style="@style/bigred"
/>

(from Styles/NowStyled/app/src/main/res/layout/main.xml)

Note that the style attribute is part of stock XML and therefore is not in the android namespace, so it does not get the android: prefix.

The value, @style/bigred, points to a style resource. Style resources are values resources and can be found in the res/values/ directory in your project, or in other resource sets (e.g., res/values-v11/ for values resources only to be used on API Level 11 or higher). The convention is for style resources to be held in a styles.xml file, such as the one from the NowStyled project:

<?xml version="1.0" encoding="utf-8"?>
<resources>
  <style name="bigred">
    <item name="android:textSize">30sp</item>
    <item name="android:textColor">#FFFF0000</item>
  </style>
</resources>
(from Styles/NowStyled/app/src/main/res/values/styles.xml)

The <style> element supplies the name of the style, which is what we use when referring to the style from a layout. The <item> children of the <style> element represent values of attributes to be applied to whatever the style is applied towards — in our example, our Button widget. So, our Button will have a comparatively large font (android:textSize set to 30sp) and have the text appear in red (android:textColor set to #FFFF0000).

Just defining the style and applying it to the widget gives us the desired results:

The Styles/NowStyled sample application
Figure 186: The Styles/NowStyled sample application

Elements of Style

There are four elements to consider when applying a style:

Where to Apply a Style

The style attribute can be applied to a widget, to only affect that widget.

The style attribute can be applied to a container, to affect that container. However, doing this does not automatically style its children. For example, suppose res/layout/main.xml looked instead like this:


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    style="@style/bigred">               
  <Button
    android:id="@+id/button"
    android:text=""
    android:layout_width="match_parent"
    android:layout_height="match_parent"
  />
</LinearLayout>

The resulting UI would not have the Button text in a big red font, despite the style attribute. The style only affects the container, not the contents of the container.

You can also apply a style to an activity or an application as a whole, though then it is referred to as a “theme”, which will be covered a bit later in this chapter.

The Available Attributes

When styling a widget or container, you can apply any of that widget’s or container’s attributes in the style itself. So, if it shows up in the “XML Attributes” or “Inherited XML Attributes” portions of the Android JavaDocs, you can put it in a style.

Note that Android will ignore invalid styles. So, had we applied the bigred style to the LinearLayout as shown above, everything would run fine, just with no visible results. Despite the fact that LinearLayout has no android:textSize or android:textColor attribute, there is no compile-time failure nor a runtime exception.

Also, layout directives, such as android:layout_width, can be put in a style.

Inheriting a Style

You can also indicate that you want to inherit style attributes from another style, by specifying a parent attribute on the <style> element.

For example, take a look at this style resource:


<?xml version="1.0" encoding="utf-8"?>
<resources>
  <style name="activated" parent="android:Theme.Holo">
    <item name="android:background">?android:attr/activatedBackgroundIndicator</item>
  </style>
</resources>

(note: in some renditions of this book, you may see the <item> element split over two lines — this is caused by word-wrapping, as this element should be all on one line)

Here, we are indicating that we want to inherit the Theme.Holo style from within Android. Hence, in addition to all of our own attribute definitions, we are specifying that we want all of the attribute definitions from Theme.Holo as well.

In many cases, this will not be necessary. If you do not specify a parent, your attribute definitions will be blended into whatever default style is being applied to the widget or container.

That ?android:attr looks a bit bizarre, but we will get into what that syntax means in the next section.

The Possible Values

Typically, the value that you will give those attributes in the style will be some constant, like 30sp or #FFFF0000.

Sometimes, though, you want to perform a bit of indirection — you want to apply some other attribute value from the theme you are inheriting from. In that case, you will wind up using the somewhat cryptic ?android:attr/ syntax, along with a few related magic incantations.

For example, let’s look again at this style resource:


<?xml version="1.0" encoding="utf-8"?>
<resources>
  <style name="activated" parent="android:Theme.Holo">
    <item name="android:background">?android:attr/activatedBackgroundIndicator</item>
  </style>
</resources>

Here, we are indicating that the value of android:background is not some constant value, or even a reference to a drawable resource (e.g., @drawable/my_background). Instead, we are referring to the value of some other attribute — activatedBackgroundIndicator — from our inherited theme. Whatever the theme defines as being the activatedBackgroundIndicator is what our background should be.

This portion of the Android style system is very under-documented, to the point where Google itself recommends you look at the Android source code listing the various styles to see what is possible.

This is one place where inheriting a style becomes important. In the example shown in this section, we inherited from Theme.Holo, because we specifically wanted the activatedBackgroundIndicator value from Theme.Holo. That value might not exist in other styles, or it might not have the value we want.

Themes: Would a Style By Any Other Name…

Themes are styles, applied to an activity or application, via an android:theme attribute on the <activity> or <application> element. If the theme you are applying is your own, just reference it as @style/..., just as you would in a style attribute of a widget. If the theme you are applying, though, comes from Android, typically you will use a value with @android:style/ as the prefix, such as @android:style/Theme.Holo.Dialog or @android:style/Theme.Holo.Light.

In a theme, your focus is not so much on styling widgets, but styling the activity itself. For example, here is the definition of @android:style/Theme.Holo.NoActionBar.Fullscreen:


<!-- Variant of the default (dark) theme that has no title bar and
          fills the entire screen -->
<style name="Theme.Holo.NoActionBar.Fullscreen">
  <item name="android:windowFullscreen">true</item>
  <item name="android:windowContentOverlay">@null</item>
</style>

It specifies that the activity should take over the entire screen, removing the status bar on phones (android:windowFullscreen set to true). It also specifies that the “content overlay” — a layout that wraps around your activity’s content view — should be set to nothing (android:windowContentOverlay set to @null), having the effect of removing the title bar.

What Happens If You Have No Theme

Most of the sample apps that we have examined so far have not defined a theme, either at the <application> level or the <activity> level. What happens here then depends upon the device that your app runs upon:

As a result, your app is far from “broken”, despite the lack of an explicit theme. It does mean, though, that your app will have a different look on those different Android OS levels, a look that will tend to have your app blend in more with other apps on that same device.

However, once you want to start customizing your theme, you will now run into a problem: having different themes for different OS versions. An Android 2.x device knows nothing about Theme.Material, for example, so you cannot simply create a custom theme based on Theme.Material and expect it to work. As we will see in a later chapter, the solution winds up being versioned resources, where you have different theme definitions for different API levels.

Of course, if your minSdkVersion is high enough, resource versioning is less of an issue. For example, if your minSdkVersion is 21, all devices that your app runs upon should know about Theme.Material, just as if your minSdkVersion were 11 or higher, all devices that your app would run on would know about Theme.Holo.

Android Studio’s Theme Editor

On Android Studio 1.5 and higher, there is a dedicated theme editor, which allows you to (somewhat) preview your theme and (somewhat) modify it visually.

When you open a style or theme resource, you will get a banner across the top of the XML editor, offering to open the theme in the theme editor:

The Styles/NowStyled Style Resource, with Banner
Figure 187: The Styles/NowStyled Style Resource, with Banner

Clicking the “Open editor” link in that banner will bring up the Theme Editor tab:

The Android Studio Theme Editor
Figure 188: The Android Studio Theme Editor

If the style resource does not define a style being used as a theme – as is the case with the NowStyled sample app, you wind up with a pretty, albeit read-only, way of seeing how colors and settings in the theme will affect the action bar (labeled here as the “app bar”), buttons, and so forth.

If you open the Theme Editor on a style resource that is being used as a theme, you may get a preview of that custom theme:

The Android Studio Theme Editor, For an Actual Theme
Figure 189: The Android Studio Theme Editor, For an Actual Theme

In places where you have overridden certain colors, such as the android:colorPrimary attribute for a Theme.Material-based theme, you can use a color picker to replace that color with a different value:

The Android Studio Theme Editors Color Picker Dialog
Figure 190: The Android Studio Theme Editor’s Color Picker Dialog

As the dialog notes, if you change the color in the dialog, the editor will update the associated resources to match, and show you the revised value in the preview:

The Android Studio Theme Editor, For an Revised Theme
Figure 191: The Android Studio Theme Editor, For an Revised Theme

It is possible that this tool will gain greater utility in the years to come.