If you are developing your own Android
application, maybe you would like to give it a custom skin with a
defined set of colors for all the interface elements. Here I’m going to
show how to customize Android buttons. This is the same kind of
customization I made for my own app which is called
FWebLauncher and you can
download for free from the Android Market.
In this post I’m going to quickly describe the steps that have to be
performed to obtain our result, but I recommend you to download the
whole example application with all the resource images through the link
on top of this post.
First of all, you need to decide how your buttons will look in
different states, so you’ve got to define the corresponding skin images
for the
Normal,
Pressed,
Focused and
Disabled
states. Of course, if you don’t need all the states, you can just
define some of them. In the following image you can see how our buttons
will look like:

We want the skin to be resizable, so the button can adapt its
size depending on the content (i.e. the text) without making the skin
look weird. To do this, we need to define a
9-patch for each image. There’s a tool installed with the Android SDK that you can find in
${ANDROID_SDK_INSTALL_DIRECTORY}\tools\draw9patch.bat. Execute that batch file and you’ll have the following application running:

As you can see I’ve already loaded the PNG file for the
Normal
state of our buttons and I’ve defined the 9-patch with the black lines
that you can see on every side of the image. As written in the
Android developer’s documentation,
the top and left lines define the stretchable area used to resize the
image, while the bottom and right lines define the area where the
content must be placed. With the 9-patch tool, I created a new file for
each image of the button skin, so for example, if the skin image for the
Normal state of the button is called
button_normal.png, then the corresponding 9-patch file will be
button_normal.9.png and we need only this inside the resources folders of our application.
The next thing to do is creating a
selector that tells Android how it should deal with the skin images depending on the button state. The
selector is just an XML file that looks like this:
1 | <?xml version="1.0" encoding="utf-8"?> |
2 | <selector xmlns:android="http://schemas.android.com/apk/res/android"> |
3 | <item android:drawable="@drawable/button_disabled" android:state_enabled="false"/> |
4 | <item android:drawable="@drawable/button_pressed" android:state_pressed="true"/> |
5 | <item android:drawable="@drawable/button_focused" android:state_focused="true"/> |
6 | <item android:drawable="@drawable/button_normal"/> |
In the
selector you define which skin image should be used to render each button state. So if the
selector XML file is called
button.xml, you can just write something like this to have your button skin working:
1 | <Button android:id="@+id/textBtn" |
2 | android:layout_width="wrap_content" |
3 | android:layout_height="wrap_content" |
4 | android:text="Text in the button" |
5 | android:background="@drawable/button" |
6 | android:textColor="#ffffffff"/> |
This is just a simple button with some text in it. In case you need a
button with an image, like an icon, and you want it to resize
proportionally, then you don’t need to define a 9-patch and you need to
use something different like an
ImageButton:
1 | <ImageButton android:id="@+id/iconBtn" |
2 | android:layout_width="64dip" |
3 | android:layout_height="64dip" |
4 | android:src="@drawable/icon_button" |
5 | android:scaleType="fitCenter" |
6 | android:background="#00000000"/> |
These are the possible states for
iconBtn:

I set the
scaleType attribute to make the image resize correctly and I set the
background to
#00000000 because I want to make it completely transparent (remember that the format for that attribute is
ARGB where
A
is the alpha value). I don’t need the background because I already have
an image for each state of the button with its own trasparency
correctly set on the borders to have a custom shape for the button if I
need it. The
src attribute specifies the selector to use for this button:
1 | <?xml version="1.0" encoding="utf-8"?> |
2 | <selector xmlns:android="http://schemas.android.com/apk/res/android"> |
3 | <item android:drawable="@drawable/icon_button_disabled" android:state_enabled="false"/> |
4 | <item android:drawable="@drawable/icon_button_pressed" android:state_pressed="true"/> |
5 | <item android:drawable="@drawable/icon_button_focused" android:state_focused="true"/> |
6 | <item android:drawable="@drawable/icon_button_normal"/> |
What if you want a button with both an icon and the text? Well, you can do it with something like this:
1 | <?xml version="1.0" encoding="utf-8"?> |
2 | <Button android:id="@+id/textAndIconBtn" |
3 | android:layout_width="wrap_content" |
4 | android:layout_height="wrap_content" |
5 | android:text="Text in the button" |
6 | android:background="@drawable/button" |
7 | android:drawableLeft="@drawable/star_icon" |
8 | android:drawablePadding="5dip" |
9 | android:textColor="#ffffffff"/> |
The icon is specified in the
drawableLeft attribute and you can set also a padding to use between the icon and the text through the
drawablePadding attribute. In this case the
button.xml
selector makes it possible to change only the background image
depending on the button state, but not the icon itself. To do that, you
need to act programmatically in the source code.
To see the final result of what I explained, take a look at the example application downloadable from the top of this post:

For each kind of button, you can try to make it pressed, focused
or disabled to see how it changes. The layout of the main activity is
defined as follows:
001 | <?xml version="1.0" encoding="utf-8"?> |
002 | <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |
003 | android:orientation="vertical" |
004 | android:layout_width="fill_parent" |
005 | android:layout_height="fill_parent"> |
008 | android:layout_width="wrap_content" |
009 | android:layout_height="wrap_content" |
010 | android:text="Button with text:"/> |
013 | android:orientation="horizontal" |
014 | android:layout_width="wrap_content" |
015 | android:layout_height="wrap_content" |
016 | android:layout_marginTop="3dip" |
017 | android:layout_marginBottom="3dip"> |
019 | <Button android:id="@+id/textBtn" |
020 | android:layout_width="wrap_content" |
021 | android:layout_height="wrap_content" |
022 | android:text="Text in the button" |
023 | android:background="@drawable/button" |
024 | android:textColor="#ffffffff" |
025 | android:layout_gravity="center_vertical"/> |
027 | <CheckBox android:id="@+id/textBtnEnabledCheck" |
028 | android:layout_width="wrap_content" |
029 | android:layout_height="wrap_content" |
030 | android:layout_marginLeft="10dip" |
031 | android:checked="true" |
032 | android:text="Enabled" |
033 | android:layout_gravity="center_vertical"/> |
038 | android:layout_width="wrap_content" |
039 | android:layout_height="wrap_content" |
040 | android:text="Button with icon:" |
041 | android:layout_marginTop="6dip"/> |
044 | android:orientation="horizontal" |
045 | android:layout_width="wrap_content" |
046 | android:layout_height="wrap_content" |
047 | android:layout_marginTop="3dip" |
048 | android:layout_marginBottom="3dip"> |
050 | <ImageButton android:id="@+id/iconBtn" |
051 | android:layout_width="64dip" |
052 | android:layout_height="64dip" |
053 | android:src="@drawable/icon_button" |
054 | android:scaleType="fitCenter" |
055 | android:background="#00000000" |
056 | android:layout_gravity="center_vertical"/> |
058 | <CheckBox android:id="@+id/iconBtnEnabledCheck" |
059 | android:layout_width="wrap_content" |
060 | android:layout_height="wrap_content" |
061 | android:layout_marginLeft="10dip" |
062 | android:checked="true" |
063 | android:text="Enabled" |
064 | android:layout_gravity="center_vertical"/> |
069 | android:layout_width="wrap_content" |
070 | android:layout_height="wrap_content" |
071 | android:text="Button with text and icon:" |
072 | android:layout_marginTop="6dip"/> |
075 | android:orientation="horizontal" |
076 | android:layout_width="wrap_content" |
077 | android:layout_height="wrap_content" |
078 | android:layout_marginTop="3dip" |
079 | android:layout_marginBottom="3dip"> |
081 | <Button android:id="@+id/textAndIconBtn" |
082 | android:layout_width="wrap_content" |
083 | android:layout_height="wrap_content" |
084 | android:text="Text in the button" |
085 | android:background="@drawable/button" |
086 | android:drawableLeft="@drawable/star_icon" |
087 | android:drawablePadding="5dip" |
088 | android:textColor="#ffffffff" |
089 | android:layout_gravity="center_vertical"/> |
091 | <CheckBox android:id="@+id/textAndIconBtnEnabledCheck" |
092 | android:layout_width="wrap_content" |
093 | android:layout_height="wrap_content" |
094 | android:layout_marginLeft="10dip" |
095 | android:checked="true" |
096 | android:text="Enabled" |
097 | android:layout_gravity="center_vertical"/> |
In the
MainActivity class we just define the listeners to
make the buttons enabled or disabled depending on the checkboxes. Note
that you need to set the value also for the
clickable property of the
ImageButton to make it actually enabled or disabled.
01 | package com.devahead.customandroidbuttonwithresizableskin; |
03 | import android.app.Activity; |
04 | import android.os.Bundle; |
05 | import android.widget.Button; |
06 | import android.widget.CheckBox; |
07 | import android.widget.CompoundButton; |
08 | import android.widget.CompoundButton.OnCheckedChangeListener; |
09 | import android.widget.ImageButton; |
11 | public class MainActivity extends Activity implements OnCheckedChangeListener |
14 | protected Button textBtn; |
15 | protected CheckBox textBtnEnabledCheck; |
16 | protected ImageButton iconBtn; |
17 | protected CheckBox iconBtnEnabledCheck; |
18 | protected Button textAndIconBtn; |
19 | protected CheckBox textAndIconBtnEnabledCheck; |
22 | public void onCreate(Bundle savedInstanceState) |
24 | super.onCreate(savedInstanceState); |
25 | setContentView(R.layout.main); |
28 | textBtn = (Button)findViewById(R.id.textBtn); |
29 | textBtnEnabledCheck = (CheckBox)findViewById(R.id.textBtnEnabledCheck); |
30 | iconBtn = (ImageButton)findViewById(R.id.iconBtn); |
31 | iconBtnEnabledCheck = (CheckBox)findViewById(R.id.iconBtnEnabledCheck); |
32 | textAndIconBtn = (Button)findViewById(R.id.textAndIconBtn); |
33 | textAndIconBtnEnabledCheck = (CheckBox)findViewById(R.id.textAndIconBtnEnabledCheck); |
36 | textBtnEnabledCheck.setOnCheckedChangeListener(this); |
37 | iconBtnEnabledCheck.setOnCheckedChangeListener(this); |
38 | textAndIconBtnEnabledCheck.setOnCheckedChangeListener(this); |
42 | public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) |
44 | if (buttonView == textBtnEnabledCheck) |
46 | textBtn.setEnabled(isChecked); |
48 | else if (buttonView == iconBtnEnabledCheck) |
50 | iconBtn.setEnabled(isChecked); |
51 | iconBtn.setClickable(isChecked); |
53 | else if (buttonView == textAndIconBtnEnabledCheck) |
55 | textAndIconBtn.setEnabled(isChecked); |
That’s it. I hope you can find something useful in this post and if
you want to see some custom buttons in action in a real application, you
could give a try to
FWebLauncher.
From : http://www.devahead.com/blog/2011/08/creating-a-custom-android-button-with-a-resizable-skin/