Android: Working with themes and styles

Simple ways to apply app-wide changes with a few lines of code

Joanne Kao
5 min readJul 18, 2017

One of the most important elements to a successful app is its design. Many developers have a natural tendency to focus on the functionality of their app and leave its appearance as an afterthought. Often I come across layouts where color, size, and other attributes are set in the declaration of each view. Suppose this is our TextView:

<TextView
android:id="@+id/someText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/purple"
android:fontFamily="cursive"
android:textSize="10sp"

android:text="This is my tiny purple text"/>

Fast-forward a couple months — our designer wants to redesign the app! Purple cursive is out, and blue monospace is in. What is 10sp — text for ants?!

So what do we do? Change all our layout files? Maybe we can extract this into a style? Is there a better way to enforce consistency and apply high-level changes?

Yes. 😀

By working with themes, updating your app’s look and feel becomes a piece of cake. It also opens the doors to a lot of cool possibilities such as supporting multiple themes within your app and changing them at runtime. Most importantly, it provides us reusable components that are easy to maintain and change as our apps become more and more complex.

I’ll be covering a few ways to customize your theme through examples. Through color attributes, text appearances, widget styling, and custom attributes, we can make high-level styling changes with just a few lines of xml.

(For brevity, I won’t get into the definitions of “what’s a theme?” , “what’s a style”, “what’s a theme overlay” and so forth. Info on these can be found in the official docs.)

Color attributes

The easiest and quickest way to provide styling to your entire app is by defining a few color attributes.

Let’s say we have the Light theme app shown below. We’re mostly using the attributes from Theme.AppCompat.Light.NoActionBar and only overriding what is necessary in themes.xml (or styles.xml — whatever your preference).

<style name="Theme.Kyoto"parent="Theme.AppCompat.Light.NoActionBar">
...
<item name="colorPrimary">@color/white</item>
<item name="colorPrimaryDark">
@color/whiteSmoke</item>
<item name="colorAccent">
@color/orangeRed</item>
<item name="colorControlNormal">
@color/onyx</item>
<item name="android:colorBackground">@color/lightGray</item>
<item name="android:colorForeground">@color/blueGreen</item>
...

</style>

Just by setting these colors, we already see our changes applied across our app and its components.

Feature image credit: Clay Banks
  • colorPrimary : The brand color most associated with your app. (see TabLayout tab color)
  • colorPrimaryDark : The color of the status bar.
  • colorAccent : Used for components in “activated” state. (see switch color, tab indicator)
  • colorControlNormal : the default state color of components. (see CheckBox, RadioButton).
  • colorBackground : the background color of the activity and other components. (see activity background)
  • colorForeground : Loose inverse of colorBackground and used for certain components. e.g. track color of SwitchCompat in its “off” state.

For brevity, I’ve excluded a few other color attributes such as colorBackgroundFloating and colorControlActivated. For more information on all the colors available, please see the list of resources linked at the end of this post.

Styling text with TextAppearance

Sometimes just changing widget colors doesn’t cut it. You want hot pink text, and you want it in comic sans. This is possible with TextAppearances.

TextAppearances (android:textAppearance) are styles applied specific to text. For this example, our goal is to change the text color across our app.

Step 1: Set custom text colors in your theme definition

Android provides different text colors attributes that can be set in your custom theme and will be used with the text appearances the framework already provides. The value of a textColor can either be a simple color, or a selector (e.g. for disabled states). Generally secondary and tertiary colors are the same RGB value as the primary color but with a different opacity (e.g. 87% and 54%).

<style name="Theme.Illini" parent="Theme.AppCompat.NoActionBar">
...
<item name="android:textColorPrimary">@color/egg</item>
<item name="android:textColorSecondary">@color/egg_87</item>
<item name="android:textColorTertiary">@color/egg_54</item>
<item name="android:textColorLink">@color/winterBlue</item>
<item name="android:textColorHint">@color/egg_54</item>
<item name="android:textColorPrimaryInverse">@color/onyx</item>
<item name="android:textColorSecondaryInverse">@color/onyx_54</item>
<item name="android:textColorTertiaryInverse">@color/onyx_38</item>
<item name="android:textColorLinkInverse">@color/lakeBlue</item>
<item name="android:textColorHintInverse">@color/onyx_38</item>
</style>

Step 2: Set text appearances in your TextViews

You can use the framework textAppearances out-of-box and the custom colors will already be reflected.

...<TextView
style="@style/TextSample"
android:text="android:textColorPrimary"
android:textAppearance="@style/TextAppearance.AppCompat.Body1"/>
...

If you want to customize even further, you can create your own textAppearances and set more attributes than just color. Just make sure to specify the a parent of your style so the color and other text attributes are still reflected:

<!-- styles.xml -->
<style name="CustomTitle" parent="TextAppearance.AppCompat.Title">
<item name="android:textAllCaps">true</item>
<item name="android:fontFamily">serif</item>
</style>
...<!-- some_layout.xml -->
<TextView
style="@style/TextSample"
android:text="This is my title"
android:textAppearance="@style/CustomTitle"/>

Example:

Here’s an example of various TextAppearance.AppCompat styles that reference the customized colors.

  • textColorPrimary : Headline, Large, Title, Subhead, Body1 (shown), Button, Menu, Action bar.
  • textColorSecondary : Caption (shown), Medium, action bar subtitle, etc.
  • textColorTertiary : Not generally used (set to textColorSecondary in material themes).
  • textColorLink : links/href attributes

For each color attribute, there’s a corresponding inverse color attribute. These don’t seem to be used much in the material styles.

App-wide widget styling

There’s a new serif in town! </dad>

Let’s say you want all your TextViews to use serif fonts. We can create a bunch of custom text appearances, but there’s a better way to handle this — android:textViewStyle.

Step 1: Define your custom style in styles.xml

<style name="TextStyle.Serif" parent="android:Widget.TextView">
<item name="android:fontFamily">serif</item>
</style>

Step 2: Set your widget style in your theme definition

<style name="MyTheme" parent="Theme.AppCompat.Light.NoActionBar">
...
<item name="android:textViewStyle">@style/TextStyle.Serif</item>
...
</style>

Note that widget styles aren’t limited to text views! Each widget has a default android:schema style that you can customize such as editTextStyle, radioButtonStyle, switchStyle, buttonStyle, etc.

Defining custom attributes

If you have theme-specific customization that doesn’t fit into any of the android schema attributes, you can easily create your own. For instance, our sample app displays different feature images for each theme.

Step 1: Define your custom attribute in attrs.xml

<declare-styleable name="CustomTheme">
<attr name="backdropDrawable" format="reference"/>
...
</declare-styleable>

The name of your styleable can be whatever you want. Keep in mind all custom attributes share the same global namespace. For instance you wouldn’t be able to have two styleables both defining a “backgroundDrawable” attr.

Step 2: Specify the value of your attr in your theme definition

<style name="Theme.Illini" parent="Theme.AppCompat.NoActionBar">
...
<item name="backdropDrawable">@drawable/fall</item>
...
</style>

Step 3: Reference the attribute in your layout file

<ImageView
android:id="@+id/mainBackdrop"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="?attr/backdropDrawable"/>

Conclusion

I’ve covered a few simple ways to make high-level styling changes to your app with very little code, but it barely skims the surface of what you can do with themes and styles. Below is a list of resources to get your started.

All the screenshots and snippets sourced from my sample app, available on Github.

Resources:
Official guide: Themes and Styles
Google I/O 2016 — Android themes & Styles Demystified
Material.io — Color Tool

Feature Image Credits:
Upsplash — Curtis Mac Newton
Upsplash — Angelina Odemchuk
Upsplash — Clay Banks

--

--