Thursday, June 17, 2010

Using Themes in Android Applications

As a developer, I understand how developers think when going about creating an application.  We start with an idea, with an end goal in mind, and start putting together pieces from the bottom-up until we reach our goal.  Like any engineer, we enjoy playing with the individual parts, tinkering with building blocks, until we have put them together to create something fantastic that works.  As much fun as this may be, experience shows that throwing together these building blocks without using a proper framework to build them is leads to bad designs, and applications which are difficult to maintain and change later on.

When it comes to design, we don't think in the same manner as a more artistically inclined designer would.  We work from the bottom up, and they work from the top down.  They will work with the entire endeavor as a whole, seeing the final product working without bothering with the individual nuances of the sum of its parts.  In their mind, those parts just work; in the engineer's mind, we think of how we can get those parts to work.  I believe this could be said about many types of work involving engineers and artists, like websites and buildings.

If we, the software developer, use too many individual parts to create our app that have been molded on their own, without concern for the other parts they will be fitting into, it is much harder to make them fit in the first place, and especially to later make changes to them. Besides, the less moving parts the better; as a software developer, it is simply good design to create your user interface with building blocks which conform together instead of changing numbers, colors, etc. here and there to force everything to eventually look good.

What I am dealing with here is UI; the graphical representation of your app. More specifically, UI design in Android applications.

Use Styles in UI

In the above screenshots from the sample application, you can see the exact same layout rendered differently by applying different themes.

In Android, you use Views to design your Activities.  An Activity is the central class which handles a specific, well, activity, in your application.  The class which handles the visual representation of activities is View. The Views themselves can be created programmatically within the Activity, but one should only do so in rare circumstances.  The better way to define them is using XML layout files.

The Android SDK for Eclipse gives us a nice visual editor to design views, which directly manipulates the XML for us.  The layout editor does not, unfortunately, give an easy way to customize the look and feel of the application.  By look and feel I mean the colors, the margins, the fonts, and any attributes that contribute to how all of the views display.

Remember in your beginner computer science classes learning about constants, and why you should use them?  One reason was that if you use a constant, you can reuse it in many places, but only have to change it one place.  In the same manner, we should be using constants to define the way our application looks, so we can easily change it from one central place, in order to change the entire application.  (On a side note, if you've ever dealt with creating web pages, you probably have realized that it is far better to use CSS than to change the attributes of individual elements of your HTML pages).

Creating Themes

Finally, onto the real meat of how to use themes!  What I will show you here is how to define a theme in your resources in an XML file, how to define attributes of the theme, how to apply those to your layout files, and finally how to dynamically change the theme of an activity.

Create a file themes.xml in res/values/.  Here you will create the name of your theme, and any customizable attributes you would like for your theme to define.

<?xml version="1.0" encoding="utf-8"?>
<resources>

    
<style name="Theme" parent="android:Theme">
        <item name="android:windowTitleSize">20dip</item>
    
</style>
    
</resources>

Above you see I have created a theme called Theme, whose parent is the default Android theme. There is a custom attribute called pageMargin, and also a default OS attribute android:windowTitleSize which has been overridden. I recommend you set the theme's parent to the default Android OS theme, in order to inherit all of the system attributes. Otherwise, unless you only use your own custom views, the system views such as ListView will be missing attributes which are defined in the system theme which are not defined in your own theme. This will cause funny layout issues at best, but at worst it will cause exceptions due to attributes that the views could not find. Remember, not all Android devices are built the same.

Setting the Theme

We can set the theme of either our application or individual activities in the manifest file.  If you don't need to have more than one theme at all in your application, just set the theme of the activity.  Otherwise, you can define themes specific to an activity.

<application
    
android:icon="@drawable/icon"
    
android:label="@string/app_name"
   
 android:theme="@style/Theme">

You can also set the theme of an activity programmatically.  This is done by calling setTheme() in the activity's onCreate() method, before any call to setContentView().  Note that this approach should typically be avoided, especially from the main activities of your application, because the theme you set here may not be used for any animations the system uses to show the activity (which is done before your activity starts).  The exception to this rule is if you want to set your themes dynamically, such as in the case where the application can have more than one theme.

Custom Attributes

Although it is nice to be able to override the default system properties in some cases, what we'd really like to do is define custom properties of our own in our application's layouts.  Say we wanted the margins of all of our activities to be a certain dimension.  If we define it in one place, we can change it all at once. Below is an example of a custom attribute added to our custom theme in themes.xml which we can use to define a property called pageMargin:

        <item name="pageMargin">2sp</item>

If you simply copy the above text into your new file themes.xml, you will get a build error Error: No resource found that matches the given name: attr 'pageMargin'.  This is because we have not defined what pageMargin is to the build system.  android:windowTitleSize is OK, because it is already defined in the Android SDK.

Create a file called attrs.xml in res/values/.  Here you will create your style attributes, which are any customizable attributes you would like for your theme to define.  Below, I have created a new attribute called pageMargin, which I will use to define margins.

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

Now, the build system will not throw an error anymore because pageMargin is now defined. The format attribute indicates what type of values I can define for pageMargin; in this case, either a reference to another attribute, or a dimension such as 2sp or 4px.  Other examples of possible formats are color, boolean, integer, and float.

Now, I could set the margins in my views by referencing a single constant, instead of putting the same value piecemeal around the code:

<TextView
    
android:layout_width="fill_parent"
    
android:layout_height="wrap_content"
    
android:text="Hello"
    
android:layout_margin="?pageMargin"/>

I can change pageMargin in one place and all of the margins in my views which reference this attribute will be changed.  Unfortunately, this method of defining your view's styles has some issues.  If you open this view in the Eclipse layout editor, you will see an error: Unable to resolve dimension value "?pageMargin" in attribute "layout_margin".  In order to make any use of these attributes, we need to apply the theme to our activities, and this doesn't work in the layout editor.
If you'd like to see some more examples, you can look at the Android OS source code default res directory, in particular themes.xmlstyles.xml, and attrs.xml.

Styles

A more sophisticated method to setting the properties of your views in the layout is give a view a style, which is a group of attributes, instead of defining the values of individual attributes. For example, you could set the styles of all of your title TextViews to have the style textTitle.  This style could have custom text color, font, and margin properties. In addition, the layouts can be safely edited in the Eclipse layout editor, because the style attribute is not needed to render the views.

    <TextView
        
android:layout_width="fill_parent"
        
android:layout_height="wrap_content"
        
android:text="@string/title_text"
        
style="?textTitle"/>

Now this TextView has a custom style. You can assign multiple attributes to the textTitle style, such as android:textColor and android:textSize.

When using the style attribute in your layout files, the views will appear plain when using the Eclipse layout editor.  However, when the theme is applied in your application, your custom styles will be applied.

Dynamic Themes

If you want your application to have a customizable look and feel, then you will want to create multiple styles. An example of this is given in the sample application below. To have multiple themes, you will want to create multiple theme definitions in themes.xml.

    <style name="Theme.White">
      
<item name="textTitle">@style/text_title_wh</item>
      
<item name="textSubheader">@style/text_subheader_wh</item>
    
</style>

    <style name="Theme.Blue">
      
<item name="textTitle">@style/text_title_bl</item>
      
<item name="textSubheader">@style/text_subheader_bl</item>
    
</style>

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.
If you have a theme name with a dot in it, such as MyTheme.Blue, then you will create a theme called MyTheme_Blue whose parent is the theme MyTheme. It is a convenient way to inherit the properties of a parent theme.

Sample Application

This sample program shows how to use custom themes. The application has one Activity with one corresponding layout file. Most of the views in the layout have a custom style. You can change the style dynamically by pressing the buttons at the bottom of the screen.

Project Source Code - Themes.zip (16.4 Kb)
Project Package - Themes.apk (28.1 Kb)
Screenshots

Conclusion

1. Define one or more themes in themes.xml and set the definitions of your styles there.
2. Define custom attributes, a.k.a. custom styles, in attrs.xml.
3. Describe what the values of your custom styles are in styles.xml.
4. In your layout files, give your views a style attribute, which has a custom style name as their values.
5. Set the theme of your application or activity in either AndroidManifest.xml or in the Activity's onCreate().

As an Android application programmer you should now know how to stop placing look and feel attributes directly into your layout files and start using styles to design your UI.

60 comments:

Jose Luis Ayerdis Espinoza said...

Nice but i though you were going to post something on ColorStateList but this clear me some doubts... thanks

Anonymous said...

Great tutorial, i was waiting for that for months.

Matt Quigley said...

@Jose Espinoza

There are some examples of the ColorStateList in the sample program. See the res/color/ directory. Basically, use the ColorStateList anywhere you'd use a regular color, and the colors will change depending on whether the containing View is focused, pressed, etc.

Rob said...

This is a brilliant tutorial Matt, Thanks

moonesh said...

Nice explanation.

Though I have not tried, but i assume the style overriding works in a similar fashion as web or are there some gotchas?

Anonymous said...

Extremely useful! Was exactly the use case I was looking at. Julia Child would be proud...

Richard said...

This was an amazing example. Clean, clear and functional! Thank you so much!

swapnil.kolhapur said...

Can you help me i want to modify theme for DialogPrefernce used in ListPreference
want to change radio button images and background of dialog.
There is attribute defined in attrs.xml

so how do i find out the theme defined for the dialog?

Eric Harlow said...

Great tutorial. Thanks! by the way what do you use to do your syntax highlighting?

Matt Quigley said...

@Eric Harlow Actually, it's just Eclipse syntax highlighting. When you copy anything from the Eclipse editor, it copies it in RTF format. Then, I run a script which formats it in Blogger.

Jeffrey Blattman said...

this is exactly what i want, but i'm having trouble. the only diff is that i'm trying this with a widget.

i set the theme at the application level in the manifest, and tried calling Context.setTheme() from the widget's buildUpdate() method. neither has an effect. the view widget just don't see the style="?whatever" styles, although there is no error at design time from eclipse.

any ideas?

atraudes said...

This was by far the most useful tutorial on themes in Android I've found yet. Thank you!

I was in the middle of implementing themes based off of this when I ran into this problem and was hoping you could shed some light on my situation

http://stackoverflow.com/questions/4035936/tag-that-references-current-theme-in-xml-causes-force-close

Anonymous said...

this is good meat on themes for a hungry man like me.

Anonymous said...

How can one also change the default string with the theme? So, for example, the title bar could say "Blue Theme" or "Gray Theme" when you switch themes. Is it possible to define this in the XML?

Piyush Patel said...

Indeed usefull,easy for all developers. thanks

drsounds said...

How I do apply a custom theme to whole my android user interface on my phone?

Sometimes I want black on white instead of white on black

Psykna said...

Awesome, thanks a lot it really helps!!!

Anonymous said...

This a good example. But if the themes are not pre-define in theme.xml, how to implement it. That is to say, the themes are from another apk, not in the main apk. The example is like HCT HD Skin.

Suraj said...

Thank u dude.Really very nice example with good explanation

Anonymous said...

great tutorial!

i'm having the problem of when i apply style to listView it breaks on onClick events.

https://groups.google.com/group/android-developers/browse_thread/thread/46bb94300cf4bf75

any ideas?

Anonymous said...

Thanks a lot very simple and helpful

Alexis said...

Hi,

thank's for this tutorial.

Did you try custom style/theme for spinner too ?
When I modify spinner background (by xml attribute), it works but the selector arrow (on the right of first item when spinner is collapsed) becomes invisible ...
Same unexpected result with spinnerStyle attribute.

I am using API level 3 (Android 1.5 target).

Thank's for any idea.

Marek Stój said...

Thanks for this post! It is very helpful.

ashish bhandari said...

Awesome man.. i owe you a beer...

Richard Lalancette said...

I have been looking for a way to do this for this afternoon.

Thanks a bunch! That is a life saver!

Richard L.
http://android9patch.blogspot.com/

Anonymous said...

what is your email id , so that i can contact you.

Anonymous said...

Hi Mat,

Will you please add Tabwidget style to the Theme.white and Theme.blue. I am using your theme having a listview inside a tabwidget and the colors dont match.

Thanks

Raianeh

Anonymous said...

Hi Matt Quigley !
Thanks for the tutorial.
I'm following your tutorial. And here I have two themes in my application. I tried to adapt my application but in the Util class when i put activity.setTheme (R.style ta ... the theme xml on my values ​​does not appear. In your example appears Theme.White and Theme.Blue. You know how to solve this problem ?

Anonymous said...

hello matt if I have more than one activity how can I change the theme at all?

Joel said...

Thanks for the article!

Has anyone figured out how to either share themes across apps dynamically or load themes from a website?

The idea is that other developers could create a module to theme your app.

Anonymous said...

I love you and want to have your babies. PS. Can I use this in one of my apps?

Munazza said...

Best tutorial , i have ever seen..

Dep said...

Great Work........

Antonis kaklis said...

Great tutorial! It help me a lot. Thank you

LeBzul said...

Nice tutorial.
Thanks!

Mikooo said...

I've sth like this applied to TextView textColor.






But it's always red instead of my colors. When I set textColor just to ?foreground color changes to foreground correctly. So there is issue with that selector? It doesn't know about references to the actual color?

Mikooo said...

I've sth like this applied to TextView textColor.
selector xmlns:android="http://schemas.android.com/apk/res/android">
item android:state_pressed="true" android:color="?background"/>
item android:state_focused="true" android:color="?background"/>
item android:color="?foreground"/>
/selector>

But it's always red instead of my colors. When I set textColor just to ?foreground color changes to foreground correctly. So there is issue with that selector? It doesn't know about references to the actual color?

Rivien Media LLC said...

Great information! After implementing into an actual application I noticed the call to "Utils.onActivityCreateSetTheme(this)" would not work correctly if placed after the "super.onCreate(savedInstanceState);" line in my Activity "onCreate" routine. After searching the Internet I discovered placing the line before "super.onCreate(savedInstanceState);" worked fine.

na389 said...

Just as here in the example textview styles are being changed dynamically... Is it possible to use the similar technique with action bar in ICS that too without relaunching the activity..

Vishnu Suresh said...

Hello,i'm so much thankful to the author of this blog...
Bcoz this thread gave me not only an answer to my search for android design using themes,but also the method for creating and returning the android widgets from a POJO without extending Activity.Thank u a lot...

Dmitry. V. Zemnitskiy said...

as I c in your example, pageMargin attribute is not actually used, and I was unable to modify your application to reference it to set, for example, paddings of main layout.

This causes "java.lang.UnsupportedOperationException: Can't convert to dimension: type=0x2"

From the other hand, referencing any attribute declared in android package works ok

I spent lot of time googling for this problem but just found number of unanswered questions like this.

Hemangi said...

This was by far the most useful tutorial on themes in Android I've found yet. Thank you!

Nicolas said...

Thanks

Som said...

Awesome tutorial .I was Just searching the source codes since long time & thanks a lot for the details and step by step method you have explained here .

Unknown said...

Hi there,

at what API level does this start?

Cheers

Marco Maddo said...

Cool post, I was look for something about Themes for Android App.

Congratulations, It's works!

Cheers.

Marco Maddo
Brazil
http://www.4android.com.br

brenagwynn said...

Holy... Man, works like magic! Thanks a lot!

Theme Spinner said...

It is useful. I did follow the guideline and I have a perfect setup.
Thanks

nilkash said...

Thanks a lot for such a good post

SAURABH said...

Learn how to create style for a view in Android visit here on
www.driveandroid.com

Anonymous said...

This is bad example. Instead of code beiing shown on wep page one must download it, unzip and then view it with some tool. Waste of time.
But I believe it was done with best intensions.

Anonymous said...

Nice tutorial. In my case, I would like to programmatically create dynamic list populated from database. I am able to create the text view but I can’t reproduce the View. All stuck with java.lang.NullPointerException. The Code I use is :

View vi = new View(Home.this);
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) vi.getLayoutParams();
params.height = 1;
vi.setLayoutParams(params);
linearLayout.addView(vi);

Anonymous said...

This tutorial is a GOD SEND!! Thank you so much for making it so easy to understand for a designer like myself!! XXXX

Nayyereh said...

Thank you for this useful tutorial.

Yunus Luniwala said...

Nice tutorial.
In my case, I am creating some layouts dynamically depending on the conditions. So how can I handle theme change in such cases.
For example, in one of my activity, I am creating the whole layout dynamically, which includes LinearLayout,Button and other widgets. Please suggest me to do this smoothly.
Thanks

Yunus Luniwala said...
This comment has been removed by the author.
Yunus Luniwala said...
This comment has been removed by the author.
Augusto Ícaro Farias da Cunha said...

Thank you for this useful tutorial. :D

Bill Mote said...

So, you wrote this almost 4 years ago. What have you learned since then that would make this information better? I'd love to see a deeper dive into the subject, how/why it works, etc.

Anonymous said...

Great tutorial, Thank you very much!