Android custom skinned Button – Tutorial

Posted by

Introduction

Some Android phone manufacturers replace the default themes, and styles, and drawable assets with what they think looks good and customized. Unfortunately, the side effect of this customization is that what works great in the emulator, and most phones simply does not work these devices. Eg, Motorola Droid 2 and X have a customized theme that uses really dark backgrounds, and red foreground colors. This can wreck many applications that are designed for a light background with dark text color.

Using a single image as a button background

The simple way to resolve this situation is to copy some themes, styles, and drawables from the SDK and use them in your code. A simple example is using custom resources for a Button. Here are the steps:

  1. Locate the original assets in your SDK folder, eg: C:androidandroid-sdk_r06-windowsplatformsandroid-3data
  2. Locate the NinePatch PNG files that already exist there, search for this filename: “btn_*default*9.png”. This is better than creating your own from scratch. You can just modify these existing assets, or get some ideas from them before making your own. Here is a the SDK tool that allows you to create these PNG assets. Just open up one of the defaults to figure out how you can do this.

The Button can use just 1 NinePatch asset that you can load like this (resid is simple R.drawable.<your png file>):

Button button = new Button(...);
button.setBackgroundDrawable(
  (NinePatchDrawable) ctx.getResources().getDrawable(resid)
);

Here is an example of a PNG that you can copy:

btn_default_pressed.9.png

There are some limitations with this approach. The button looks the same whether it’s focused, disabled, pressed, etc. So this doesn’t quite look right when you use this button.

Using a StateListDrawable as a button background

Android provides StateListDrawable just for this scenario. You can easily use different NinePatch images for the background. It is not easy to create this kind of object programatically, so you have to use XML. You have to create an XML file in your app’s res/drawable folder. Eg: ”zen_button_red.xml”

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

<!--

this is a multistate button, with the individual PNGs being 9patch buttons.

-->

<selector xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:state_pressed="true"
          android:drawable="@drawable/zen_button_red_pressed">
    </item>

    <item android:state_focused="true"
          android:drawable="@drawable/zen_button_red_focused">
    </item>

    <item
            android:drawable="@drawable/zen_button_red_normal">
    </item>

</selector>

Now you have to add the PNGs in “@drawable” in the res/drawable folder. Here’s an example:

zen_button_red_focused.9.png zen_button_red_normal.9.png zen_button_red_pressed.9.png
zen_button_red_focused.9.png zen_button_red_normal.9.png zen_button_red_pressed.9.png

Now, in order to use this state list drawable, you can just do the following:

button.setBackgroundResource(R.drawable.zen_button_red);

Closing thoughts

This strategy of copying existing layouts, styles, themes, PNGs, etc will allow you to create customized user interfaces that will not break when a phone manufacturer swaps out the default themes with things that will otherwise break your user interface. The NinePatch image will scale regardless of the size of the screen and dpi, and of course, the state list drawable is a convenient way to package multiple NinePatch images for different states.