Sunday, August 11, 2013

Applying styles and themes in Android applications

Very often you want to create applications that the user can customize according to his needs or aesthetics. For instance, if you develop a sports application about football you may have an option on settings in order the user to apply a theme to the application based on his favorite team.

In this post I am gonna show you how to apply different themes and styles to your applications. First of all, 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. Styles in Android share a similar philosophy to cascading stylesheets in web design—they allow you to separate the design from the content.

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.

To create a set of styles, save an XML file in the res/values/directory of your project, usually under the name styles.xml, but the name of the XML file is arbitrary. The root node of the XML file must be <resources>. For each style you want to create, add a <style> element to the file with a name that uniquely identifies the style (this attribute is required). Then add an <item> element for each property of that style, with a name that declares the style property and a value to go with it (this attribute is required). Below is displayed a single style:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="textViewStyle_de">
<item name="android:text">This is default theme</item>
<item name="android:textColor">#FFFFFF</item>
<item name="android:textSize">12sp</item>
</style>
</resources>
Each <style> element is converted into an application resource object at compile-time, which can be referenced by the value in the <style> element's name attribute. This example style can be referenced from an XML layout as @style/textViewStyle_de. 

Now you will see how you can create different themes using a set of styles. First of all, you need to create an XML file that defines the attributes of our themes. Again, save a file in the res/values/ directory of your project under the name attrs.xml. Let's assume that our application has a different background color and a different style for the TextViews for each theme. So you need to create an attribute for each property as shown in the example below:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<attr name="themeBackground" format="reference" />
<attr name="textViewStyle" format="reference" />
</resources>
For each attribute add an <attr> element with a name that uniquely identifies this attribute and a format that indicates what type of values you can define for this attribute. Next, create an XML file that declares the different themes of the application. As before, save an XML file in the res/values/ directory of your project under the name themes.xml. The content of the file is quoted next:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Theme" parent="android:Theme">
<item name="themeBackground">@style/themeBackground_de</item>
<item name="textViewStyle">@style/textViewStyle_de</item>
</style>
<style name="Theme.Theme2">
<item name="themeBackground">@style/themeBackground_theme2</item>
<item name="textViewStyle">@style/textViewStyle_theme2</item>
</style>
</resources>
You need to declare a <style> element for each theme you want to create. In this example, there are two themes and for each one of them there is an <item> element ,with a name  for each <attr> in the attrs.xml file that the theme is going to support. The styles that are assigned as values (i.e. @style/themeBackground_de) are stored in styles.xml in the res/values/directory of the project.
<?xml version="1.0" encoding="utf-8"?>
<resources>

<!-- =============================================================== -->
<!-- Default styles -->
<!-- =============================================================== -->

<style name="themeBackground_de">
<item name="android:background">#FF0000</item>
</style>

<style name="textViewStyle_de">
<item name="android:text">This is default theme</item>
<item name="android:textColor">#FFFFFF</item>
<item name="android:textSize">12sp</item>
</style>

<!-- =============================================================== -->
<!-- Theme2 styles -->
<!-- =============================================================== -->

<style name="themeBackground_theme2">
<item name="android:background">#008000</item>
</style>

<style name="textViewStyle_theme2">
<item name="android:text">This is theme 2</item>
<item name="android:textColor">#EE7600</item>
<item name="android:textSize">15sp</item>
</style>

</resources>
Finally just set the style property on XML elements in the layoutfile:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
style="?themeBackground" >

<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
style="?textViewStyle"/>

<Button
android:id="@+id/theme_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/toggle_theme"/>

</LinearLayout>
To set the theme dynamically at runtime, call setTheme() in your activity's onCreate() method, before calling setContentView(). To change the theme, you simply need to restart your activity. Activity's code is displayed below:
package com.androidsnippet.Themes;

import android.app.Activity;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class ApplicationThemesActivity extends Activity {

private final String THEME = "theme";
private final int DEFAULT_THEME = 0;
private final int THEME_2 = 1;

private SharedPreferences mSettings;
private Button mThemeButton;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mSettings = PreferenceManager.getDefaultSharedPreferences(this);

switch(mSettings.getInt(THEME, DEFAULT_THEME)) {

case DEFAULT_THEME:
this.setTheme(R.style.Theme);
break;

case THEME_2:
this.setTheme(R.style.Theme_Theme2);
break;
}

setContentView(R.layout.main);

mThemeButton = (Button) findViewById(R.id.theme_button);

mThemeButton.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View v) {
// TODO Auto-generated method stub
SharedPreferences.Editor editor = mSettings.edit();
switch(mSettings.getInt(THEME, DEFAULT_THEME)) {

case DEFAULT_THEME:
editor.putInt(THEME, THEME_2);
break;

case THEME_2:
editor.putInt(THEME, DEFAULT_THEME);
break;
}
editor.commit();

finish();
Intent intent = new Intent(ApplicationThemesActivity.this, ApplicationThemesActivity.class);
startActivity(intent);
}
});
}
}
So if you upload the project to the Android Emulator the application will have the default theme applied:
If you click the button the theme changes to Theme2!
That's it! Find the whole project here! Do not hesitate to leave a comment! Cheers!