Manipulate view touch targets with ExtendedTouchView

Android’s design guidelines often specify that touch targets for elements should be larger their actual boundaries for better usability. TouchDelegate exists for this reason, but using it is surprisingly tedious. No more, this is where ExtendedTouchView comes in!

Touch targets in a Material design dialog
The cyan area shows button boundaries, whereas the pink area shows the bounds of their touch targets.

Take the Material dialog specification for example. The buttons have a minimum height of 36 dp, but for better usability their touch areas should have a minimum height of 48 dp.

The Metrics & keylines section explains why 48 dp × 48 dp is a common minimum size for touch targets:

This is comfortably in the range of recommended target sizes (7–10 mm) for touchscreen objects and ensures that users will be able to reliably and accurately target them with their fingers.

The tedious way with TouchDelegate

The usual way to implement larger touch targets is to use TouchDelegate. Initially it looks simple enough:

Rect touchArea = new Rect();

// Set the new Rect's values to the view's hit rectangle.
view.getHitRect(touchArea);

// Manipulate the rectangle here.
// More on this below...

TouchDelegate delegate = new TouchDelegate(touchArea, view);
ViewParent parent = view.getParent();

if (parent instanceof View) {
    ((View) parent).setTouchDelegate(delegate);
}

Looking into it a bit closer, a few issues crop up:

  • The values are in pixels, but usually you’d want to use dps here.
  • The Rect specifies the touch area of a child in local coordinates relative to its parent.
  • If you just want a touch area that’s 48 dp tall, you’ll need to convert those dps to pixels, get the original touch area, and calculate how much you want to manipulate it’s top and bottom values.
  • To get the original touch area, the parent needs to complete a layout for its children. This means that simply dropping the code in Activity#onCreate won’t work.

If you’re interested, Google has released sample code for implementing the above. Basically you post a Runnable to the parent’s message queue that calculates the new touch area after everything has been laid out.

To be frank, I don’t want to do all this. I just want to tell the view that its touch target should be 48 dp tall, preferably without new code in my activity.

Enter ExtendedTouchView

The view is a subclass of FrameLayout that has some XML attributes that specify the dimensions for the child’s touch target:

<com.lnikkila.extendedtouchview.ExtendedTouchView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:touchHeight="48dp">

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/button" />

</com.lnikkila.extendedtouchview.ExtendedTouchView>

And that’s it. Behind the scenes the view reads those attributes, converts them to pixels and does all of the work with TouchDelegate so you don’t have to.

Get the code

You can get the code on GitHub and the compiled library on JCenter. Throw this in your build.gradle:

compile 'com.lnikkila:extendedtouchview:0.1.0'

If you find bugs, please report an issue on GitHub and send me a pull request if you can fix it yourself!


Related posts