Android 101: Styles and Themes

In this article we will look into what styles and themes are in Android and how to create them.

What are styles and themes in Android?

From the Android’s Developer Guide we know that:

A style is a collection of properties that specify the look and format for a View or window. A style can specify properties such as height, padding, font color, font size, background color, and much more. A style is defined in an XML resource that is separate from the XML that specifies the layout.

A theme is a style applied to an entire Activity or application, rather than an individual View. When a style is applied as a theme, every View in the Activity or application will apply each style property that it supports.

So to summarize, a style is a set of proprieties that define the way a View or window looks. A theme is a specific kind of style that is scoped at Activity or application level.

Declare and apply a style

Let’s see how to declare a style. Add this to the file res/values/styles.xml (if it doesn't already exist, create it).

<resources>
    <style name="AppTheme.Button" parent="@android:style/Widget.Button">
        <item name="android:textSize">12sp</item>
        <item name="android:paddingLeft">10dp</item>
        <item name="android:paddingRight">10dp</item>
    </style>
</resources>

This style has the parent attribute set to the Button widget. What this means is that our custom theme will inherit all the style definitions of a standard button. So if we declare nothing in our style, then our button will look exactly the same as the standard button. In this case we override the text and padding properties.

<Button
    style="@style/AppTheme.Button"
    android:text="@string/buttontext" />

What we did here is give a specific button in our application a custom style we’ve created. Something that might be more interesting than this is to give all the buttons in our application a custom look and feel. And the correct way to do this is not by changing the declaration of every single button in our app, but by creating a theme.

Declare and apply a theme

This is how to declare a theme:

<style name="AppTheme.Light" parent="@android:style/Theme.Holo.Light">
    <item name="android:textSize">14sp</item>
    <item name="android:buttonStyle">@style/AppTheme.Button</item>
    <item name="android:editTextStyle">@style/AppTheme.EditText</item>
    <item name="listItemStyle">@style/AppTheme.ListItemStyle</item>
</style>

Place this in the same place as you would a normal style (res/values/styles.xml).

Pretty much the same as the style declaration, but you might notice the difference in the parent attribute of the style. The parent here is an out of the box android theme.

What you might also notice is the second item node. Here we’re declaring the buttonStyle which will be applied to all the buttons under this theme. You can see that we’re referencing the style we’ve created before.

You can now apply this theme to an activity or to the whole application. You need to edit the AndroidManifest.xml file.

<application android:theme="@style/AppTheme.Light">
<activity android:theme="@style/AppTheme.Light ">

Use one or the other, depending on the scope you need.

Style Inheritance

In our application we have two themes, a light and a dark one. Therefore we created two theme declarations.

Light
Light
Dark
Dark
<style name="AppTheme.Light" parent="@android:style/Theme.Holo.Light">
    <item name="android:textSize">14sp</item>
    <item name="android:buttonStyle">@style/AppTheme.Button</item>
    <item name="android:editTextStyle">@style/AppTheme.EditText</item>
    <item name="listItemStyle">@style/AppTheme.ListItemStyle</item>
</style>
<style name="AppTheme.Dark" parent="@android:style/Theme.Holo">
    <item name="android:textSize">14sp</item>
    <item name="android:buttonStyle">@style/AppTheme.Button</item>
    <item name="android:editTextStyle">@style/AppTheme.EditText</item>
    <item name="listItemStyle">@style/AppTheme.ListItemStyle.Dark</item>
</style>

Besides the parent attribute you can use another declaration of inheritance through the naming convention. For instance, for the declaration of the dark theme widgets, we can inherit all the definitions from the light theme definition and only override what we need:

<style name="AppTheme.ListItemStyle.Dark">
    <item name="android:background">@color/list_item_dark</item>
</style>

AppTheme.ListItemStyle.Dark is interpreted as an inheritance from the already defined AppTheme.ListItemStyle. This is a more structured and readable way to declare parenting relationships, especially if you have several themes or widget styles.

See all available Android widgets to override here.

Create a custom style for a View

One of the proprieties on the theme definition I showed you is the listItemStyle.

<style name="AppTheme.Light" parent="@android:style/Theme.Holo.Light">
    <item name="android:textSize">14sp</item>
    <item name="listItemStyle">@style/AppTheme.ListItemStyle</item>
</style>
<style name="AppTheme.ListItemStyle">
    <item name="android:background">@color/list_item</item>
</style>

I had to create this custom attribute value because I wanted to style a custom LinearLayout view differently for each of my themes and that option wasn’t available by default in the android namespace (as the android:textSize is).

Here’s my view definition:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/feedListItem"
    android:layout_height="wrap_content"
    android:layout_width="fill_parent"
    style="?listItemStyle">
    <ImageView android:id="@+id/picture"
        android:layout_height="100dip"
        android:layout_width="100dip"/>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/user"
        android:orientation="vertical"
        android:layout_height="wrap_content"
        android:layout_width="fill_parent"
        android:padding="10dp">
        <TextView android:id="@+id/textUsername"
            android:textAppearance="?android:attr/textAppearanceSmall"
            android:layout_height="wrap_content"
            android:layout_width="fill_parent"
            android:layout_gravity="center_horizontal"
            android:textStyle="bold"/>
        <TextView android:id="@+id/textContent"
            android:layout_height="fill_parent"
            android:layout_width="fill_parent"
            android:paddingTop="6dp"
            android:maxLines="3"/>
    </LinearLayout>
</LinearLayout>

Notice the style attribute in the parent LinearLayout.

Last thing we need to do for this to work is declare the new attribute value. Otherwise you’ll get an error saying the attribute doesn’t exist:

Under res/values/attrs.xml create a new declaration:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <attr name="listItemStyle" format="reference" />
</resources>

Change the Style or Theme at runtime

Since we had two different themes, we wanted to allow the user to change the active theme through the preferences.

We save the selected preference in the application’s SharedPreferences and load it in the Activity’s onCreate method.

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    SharedPreferences prefs = HoneybuzzApplication.getSharedPreferences();
    String themeName = prefs.getString(HoneybuzzApplication.PREFERENCE_THEME, "AppTheme.Light");
    int themeId = getResources().getIdentifier(themeName, "style", getPackageName());
    setTheme(themeId);
}

You need to do this for every Activity or you can do as we did and create parent Activity that handles all the cross-app customizations. All our other Activities inherit form it.

We also need to reload the app theme after the user changes the settings. We do this in our parent Activity:

// handle updates to preferences
public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
    if (key.equals(HoneybuzzApplication.PREFERENCE_THEME)) {
        // recreate activity so theme gets updated
        this.recreate();
    }
}

More resources

Dércia Silva
Posted by Dércia Silva on November 30, 2011

Related articles