Making a custom button is simple. First of all you need to create some graphics for the different states of your button (see the Android dev guide on basic 2d Graphics).
I created a couple NinePatchDrawable image files using GIMP and the draw9patch.bat tool included in the Android SDK under <sdk-dir>/tools/. Here they are:


Android already has its own default built-in buttons which are pretty similar to these. Of course, in a real app you can give the buttons different shapes and designs. Also, since these are NinePatch images, I could have made them smaller to save space since they'd stretch the same way anyway.
To use these in my project, I created a couple XML files under my project's res/drawable directory, pressbutton.xml and longpressbutton.xml.
res/drawable/pressbutton.xml:
<selector android="http://schemas.android.com/apk/res/android">
<item state_pressed="true" drawable="@drawable/redbutton">
<item drawable="@drawable/bluebutton">
</selector>
With selectors (StateListDrawables), the object displays the first <item> underneath the <selector> tag whose attributes match the state of the object. Since the last <item> in my example doesn't have any attributes like state_pressed, it acts as a catch-all.
res/drawable/longpressbutton.xml:
<transition android="http://schemas.android.com/apk/res/android">
<item drawable="@drawable/redbutton">
<item drawable="@drawable/bluebutton">
</transition>
The <transition> (TransitionDrawable) is more of an animation definition, so you don't need attributes on the <item>s. It plays through the <item>s sequentially based on the argument you pass into the startTransition(milliseconds) method.
Now, getting TransitionDrawables to work is a bit trickier than using StateListDrawables. Well, at least it took me longer to figure out.
I made a subclass of Button called TransitionButton, like this:
public class TransitionButton extends Button {
private TransitionDrawable mTransition = null;
private Context mContext;
public TransitionButton(Context context) {
super(context);
mContext = context;
}
public TransitionButton(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
}
public TransitionButton(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mContext = context;
}
public void setTransition(TransitionDrawable transition) {
mTransition = transition;
setBackgroundDrawable(transition);
}
public void setPressed(boolean pressed) {
super.setPressed(pressed);
if (pressed && mTransition != null) {
mTransition.startTransition(1000);
} else if (!pressed && mTransition != null) {
mTransition.resetTransition();
}
}
}
To use the Button and TransitionButton, as usual I put the necessary code into the onCreate(Bundle) method of my Activity. So I have something like this:
public class StateAndTransition extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
final Context mContext = getApplicationContext();
Resources res = mContext.getResources();
TransitionDrawable transition = (TransitionDrawable) res.getDrawable(R.drawable.longpressbutton);
final Button stateButton = (Button) findViewById(R.id.statebutton);
stateButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
stateButton.setTextSize(10 + stateButton.getTextSize());
}
});
final TransitionButton longpressButton = (TransitionButton) findViewById(R.id.longpress);
longpressButton.setTransition(transition);
longpressButton.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent m) {
if (m.getAction() == MotionEvent.ACTION_DOWN) {
longpressButton.setPressed(true);
} else {
longpressButton.setPressed(false);
}
return true; // The Listener consumes the Touch action
}
});
}
}
Finally, my res/layout/main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res/talklittle.android.examples.stateandtransition"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
>
<Button android:id="@+id/statebutton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerInParent="true"
android:text="Touch me"
android:textSize="8sp"
android:background="@drawable/pressbutton"/>
<talklittle.android.examples.stateandtransition.TransitionButton
android:id="@+id/longpress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerInParent="true"
android:text="Hold me"
android:textSize="30sp"
android:background="@drawable/redbutton"/>
</LinearLayout>
Now, that will hopefully compile and run (with minor changes like adding the imports and package statements). If so, then you should see that the Button using a StateListDrawable works fine, while the TransitionButton is buggy. It looks like this at the end of the transition:

With custom Buttons though, I think most of the time you'd be using static sized images rather than NinePatch images, but that's just my opinion. I was just too lazy this time to make more than two images.
Anyway that's about it for this small tutorial. Hope it makes sense! I wrote it mostly for myself to document this procedure, since it took awhile to figure out the details.