Android View States

Touch feedback is an important part of a touch-based UI. Whenever a user touches a control, there should be some indication that it is being interacted with. This can include tactile, aural or, more typically, visual feedback. This post will be about enabling visual cues for interaction with Views on Android.

First, we’ll discuss some of the different states that a View can be in and what they mean, then I’ll quickly add how to use selectors to automatically display the correct property for the current state.

Before we begin, a TL;DR:

  • if you have interactive controls, use selectors to react to interactions
  • order your selector items keeping in mind that Android will select the first one that matches the View’s state
  • stick in a focus state, it takes like an extra 10 seconds and helps non-touch mode users!

States

States are just flags which indicate the user’s interaction with a View. This can be a transient state, like state_pressed or a “sticky” state like state_activated. If none of these are true, then the View is in its default/normal state.

Pressed

Easy-peasy, the View will be in a pressed state if the user is touching a clickable View. The clickable property can be explicitly set with an XML attribute or using View.setClickable(boolean). However, a View will automatically be made clickable when you set a View.OnClickListener on it.

Focused

The focused state is used to indicate whether the View has input focus. The View with input focus will be given input events like key strokes, and as such it’s not often useful on touch-based UIs (there are exceptions like EditText, which can be focused in touch mode). Views that are focusable and have focus states allow keyboard/dpad/switch users to locate themselves within the UI.

The Android docs say:

The selected state is used when focus (android:state_focused) is not sufficient (such as when list view has focus and an item within it is selected with a d-pad).

I can’t replicate this case except when using GridView (which doesn’t use setFocused(boolean), but setSelected(boolean)). So, I’d recommend using the focus state and using a RecyclerView with GridLayoutManager instead.

Activated

You can use the activated state to indicate that the View in question is selected. I’m not apologising for using the word selected, here’s their apology:

/**
 * Changes the activated state of this view. A view can be activated or not.
 * Note that activation is not the same as selection.  Selection is
 * a transient property, representing the view (hierarchy) the user is
 * currently interacting with.  Activation is a longer-term state that the
 * user can move views in and out of.  For example, in a list view with
 * single or multiple selection enabled, the views in the current selection
 * set are activated.  (Um, yeah, we are deeply sorry about the terminology
 * here.)  The activated state is propagated down to children of the view it
 * is set on.
 *
 * @param activated true if the view must be activated, false otherwise
 */
public void setActivated(boolean activated) {

So this can be used in situations like a ViewPager tab strip – to highlight the tab (tabView.setActivitated(true)) representing the current visible page.

I didn’t know about the activated state before I started writing this post; previously I would use the selected state for this, because it seemed semantically correct.

However, as selected and activated states are (mostly) set explicitly by us, the application developers, (as opposed to focused state, which is mostly handled by the framework) it wasn’t a big deal to swap my usage; as long as you check your app on your minimum and target platforms, and the states work correctly, you should be fine.

Enabled

Sometimes a View is disabled if you want to alert the user to the presence of an option that cannot currently be actioned, e.g. a “sign in” button that is disabled until the user enters their credentials. Having a visual cue to indicate this state prepares the user for a lack of action when they attempt to action the button.

Using selectors

So you have your ColorStateLists for View properties that take colors, and you have your StateListDrawables for View properties that take drawables.

Let’s go with the StateListDrawables so we can set the background. Before we might have had:

<TextView
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:focusable="true"
  android:background="@android:color/holo_red_dark" />

Color resources are wrapped in ColorDrawables, which is why we can use @color/blah but not #123456 for the background attribute. Anyway, let’s create a state list drawable and use that instead:

<TextView
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:focusable="true"
  android:background="@drawable/example_background" />

res/drawable/example_background.xml:

<selector xmlns:android="http://schemas.android.com/apk/res/android">
  <item android:state_pressed="true" android:drawable="@android:color/holo_blue_light" />
  <item android:state_focused="true" android:drawable="@android:color/holo_green_light" />
  <item android:drawable="@android:color/transparent" />
</selector>

The system will read the selector from the top-down, and pick the first one that matches the View’s state. The order matters. It matters. It does matter.

You can put multiple conditions in one item – in this case the View must match all the conditions for that item to be chosen:

<selector xmlns:android="http://schemas.android.com/apk/res/android">
  <item android:state_enabled="false" android:drawable="@android:color/darker_gray" />

  <item android:state_activated="true" android:state_pressed="true" android:drawable="@android:color/holo_red_light" />
  <item android:state_activated="true" android:state_focused="true" android:drawable="@android:color/holo_purple" />
  <item android:state_activated="true" android:drawable="@android:color/holo_orange_dark" />

  <item android:state_pressed="true" android:drawable="@android:color/holo_blue_light" />
  <item android:state_focused="true" android:drawable="@android:color/holo_green_light" />
  <item android:drawable="@android:color/transparent" />
</selector>

I put the disabled state first as a sort of guard clause or quick return: Views which are disabled will ignore touch/input events, so this attribute should trump all others.

You can focus on a disabled View, but it wouldn’t make much sense to do it – if a View is disabled programmatically, you should also setFocusable(false) to avoid having a focused View with no visual cue that it’s focused. Alternatively, move the state_enabled item to just above the default one so the user can focus on this non-actionable View, but at least they’ll know where they are.

Additionally, I would avoid putting too many items in a single selector – if each item corresponds to a different value, then it may be too much information to convey to the user with a single attribute (background color):

confusing tab selectors with too many colors

Instead, let’s use text color to indicate the activated state, and background color to indicate focus/pressed state:

res/color/example_text_color.xml:

<selector xmlns:android="http://schemas.android.com/apk/res/android">
  <item android:state_activated="true" android:color="#000" />
  <item android:color="#999" />
</selector>

res/drawable/example_background.xml:

<selector xmlns:android="http://schemas.android.com/apk/res/android">
  <item android:state_pressed="true" android:drawable="@android:color/holo_blue_light" />
  <item android:state_focused="true" android:drawable="@android:color/holo_green_light" />
  <item android:drawable="@android:color/transparent" />
</selector>

and the tab:

<TextView
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:focusable="true"
  android:textColor="@color/example_text_color"
  android:background="@drawable/example_background" />

and ta-da, no touch:

tabs

Leave a Reply

Your email address will not be published. Required fields are marked *

To create code blocks or other preformatted text, indent by four spaces:

    This will be displayed in a monospaced font. The first four 
    spaces will be stripped off, but all other whitespace
    will be preserved.
    
    Markdown is turned off in code blocks:
     [This is not a link](http://example.com)

To create not a block, but an inline code span, use backticks:

Here is some inline `code`.

For more help see http://daringfireball.net/projects/markdown/syntax

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>