State List Drawables

In the last article we covered the styling of a dialog box using vector drawables. In this article we are going to add a cancel button to that dialog box, and style the button. Styling widgets is a little more complex that the static styling that we’ve previously covered because widgets change state depending on whether they are disabled, have focus, are selected, clicked, etc. and they need to change visually to indicate that state to the user. State List drawables are the tool that we need to use to manage these state changes.

Let’s start by adding a button to the previous code. If you already have a working copy of the code from the previous article, then you can use that. Alternatively, you can download a working project here.

The first thing that we need to do is add add a button control inside a LinearLayout as the last child of the RelativeLayout in res/layout/main.xml:

We are putting the button inside a LinearLayout because we want to be able to style to dialog footer. One interesting thing here is the use of weightSum in the LinearLayout and layout_weight in the button. Weights are a useful way of laying out children within a layout and we are using it here to size the button to half the width of its parent. If, for example, we wanted the button to be one third the width of the parent, we would simply change the weightSum in the LinearLayout to “3″.

Next we need to wire up the button so that it closes the window when it is clicked. In the onCreate() method of MainActivity:

This now looks like this:

Dialog Box With Button

Dialog Box With Button

Next we’ll define a new drawable for the LinearLayout that we’ve just created in res/drawable/dialog_footer.xml:

This is has a solid grey fill with the bottom two corners rounded. Next we need to define a new style using this drawable, and adding some padding at the top which will balance the 5dp that will be added to the bottom because of the rounded corners:

We can apply this to the LinearLayout by setting its style attribute:

Which gives us:

A Styled Footer

A Styled Footer

Now let’s have a look at the button. As I have already mentioned, styling widgets is a little more complex than the styling that we’ve done thus far because widgets need to indicate visually when they have focus, are selected, are clicked, or are disabled.

It is worth a quick discussion about these various states because many of the problems which are encountered when styling controls are often the result of misunderstanding the widget state.

  • Pressed – when a widget such as a button is clicked
  • Focused – when the widget is highlighted via the D-pad or trackball
  • Selected – when a widget is activated, such as the active tab on atab control
  • Checkable – this is specific to widgets which can transition between a checkable and non-checkable state
  • Checked – when a widget, such as a checkbox, is checked
  • Enabled – when a widget is enabled
  • Window Focused – when the lop level window containing the widget has focus.

There are some subtle differences between these states and it is easy to use the wrong one, and find that the behaviour that you’re expecting doesn’t happen!

Now that we have an understating of the various states that a widget can have, we can define a State List drawable which assigns different drawables for the widget depending on its state. Create this at res/drawable/button.xml:

When rendering a control each line of state drawable is evaluated until a match is found, and then that drawable is used. Therefore the order of entries is vital. In this case it is important the the “pressed” state is matched before the “focused” state. If these entries were swapped, then navigating to the control using the D-pad or trackball and then clicking would still match to focused. More common problems with state drawables result from getting the entries in the wrong order.

We now need to define the drawables that this references. First res/drawable/button_normal.xml:

This is a simple grey gradient with slightly rounded corners, and some padding around the text of the button.

Next res/drawable/button_focused.xml:

This is similar to button_normal, except that it draws a green border around the button when it has focus.

And finally res/drawable/button_focused.xml:

This uses a green gradient fill.

We now need to create a style called button in res/values/styles.xml:

There is something new that we’ve done here: setting the style parent to @android:style/Widget.Button inherits the default attributes of the android button definition, but allows us to override what we like. Here we are specifying out state list drawable, and setting a couple of margins just to keep things looking balanced.

Finally we can set the style of the button in res/layout/main.xml:

We can run this to see the three states of the button:

Button Normal

Button Normal

Button Focused

Button Focused

Button Pressed

Button Pressed

That’s looking pretty good. The only thing that I’m not happy with is that the text looks fine while it’s against the grey gradient, but it becomes a little lost on the green. Maybe it would look better if the text was white when the button was pressed. Now that we know how to select different drawables for the background based upon the state, what about dynamically changing the text style based upon state changes? We’ll cover that in the next article.

The source code for this article can be downloaded here.

© 2011, Mark Allison. All rights reserved. This article originally appeared on Styling Android.

Portions of this page are modifications based on work created and shared by Google and used according to terms described in the Creative Commons 3.0 Attribution License

Creative Commons License
State List Drawables by Styling Android, unless otherwise expressly stated, is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. Terms and conditions beyond the scope of this license may be available at blog.stylingandroid.com.

10 Responses to “State List Drawables”

  1. David says:

    Very nice what you did with those boxes!

  2. zhang says:

    hey, Allison, where is the “next article” that covers how to dynamically change the text style based upon state changes? can u tell me?

    • Mark Allison says:

      The Colours article covers this.

      Regards,

      Mark

      • zhang says:

        thank u for reply,I have got it,
        but what now I need is to change the text styles including textSize not only the textColor , do you have any ideas about this ?

        • Mark Allison says:

          Do that in your style:

          <style name="button" parent="@android:style/Widget.Button">
          .
          .
          .
          <item name="android:textSize">15sp</item>
          </style>

          • zhang says:

            oh, I am sorry,
            I meant changing the textSize based on the states,
            I would like to know if there is style selector like color selector.

            • Mark Allison says:

              Not as far as I know. Dynamically changing the text size based upon the View state could have some potentially undesirable implications for layouts. Android calculates layout positions at run-time so that it is able to work on many devices and form-factors. If controls dynamically change size based on state, it could cause the layout to change drastically, so I would be extremely wary about doing anything like this as it is difficult to predict how it may render on all devices.

  3. zhang says:

    okay, I see,
    thanks

  4. Matias Grioni says:

    This article was a great help, the only thing is that i wasn’t aware what the gradient angle did and thus, what the start and endColor referred to. But besides that confusion this was very helpful.

  5. […] 2. ¬†Round buttons¬†http://blog.stylingandroid.com/archives/55#more-55 […]

Leave a Reply