Drawables / Ripple Drawable

Ripples – Part 1

At the time of writing, Google have recently announced the Android L Developer Preview which is a foretaste of some of the goodies coming in the full L release of Android. Over the coming weeks and months we’ll be covering a number of the new APIs and features being added in API 21. To start with, we’ll look at quite a subtle things that is the basis for providing touch feedback to our users: Ripples.

Whenever the user clicks a button it is desirable to provide some kind of visual feedback so that the user knows that their touch was successful. Traditionally in Android this has been handled using state list drawables to provide different coloured or shaded drawables which indicate that a control is in touched state. With the Android L Developer Preview a new touch feedback mechanism was introduced to provide this touch feedback and is loosely based on the concept of ripples on the card metaphor which features strongly in Material design. These ripples are actually really easy to implement and, as we shall see, can automatically fit in with your app theme.

Let’s start by looking at the anatomy of a Ripple. A Ripple is a new kind of Drawable object as a RippleDrawable. Being a Drawable object we can create our RippleDrawable definition in a drawable resource:

<?xml version="1.0" encoding="utf-8"?>
<ripple 
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:color="?android:colorControlHighlight" />

That’s about as basic as we can get. It is important that we use ?android:colorControlHighlight to specify the colour of our ripple because this will use a value from the app theme, and we’ll maintain consistency as a result. We’ll cover this in more detail later in the series.

To show this working we’ll use it as the background to a Layout (for this example we’ll use a FrameLayout because it’s lightweight, but any Layout will work), and we’ll display another FrameLayout below it which will have its outline drawn (the reason for this second layout will become obvious as we progress):

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 
  xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:tools="http://schemas.android.com/tools"
  android:id="@+id/layout"
  android:orientation="vertical"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:paddingLeft="@dimen/activity_horizontal_margin"
  android:paddingRight="@dimen/activity_horizontal_margin"
  android:paddingTop="@dimen/activity_vertical_margin"
  android:paddingBottom="@dimen/activity_vertical_margin"
  tools:context=".RipplesActivity">

  <FrameLayout
    android:layout_width="match_parent"
    android:layout_height="0dp"
    android:layout_weight="1"
    android:clickable="true"
    android:background="@drawable/ripple" />

  <FrameLayout
    android:layout_width="match_parent"
    android:layout_height="0dp"
    android:layout_weight="1"
    android:clickable="true"
    android:background="@drawable/frame"/>
</LinearLayout>

All we’ve done is set the background attribute of the top layout to the ripple drawable that we defined earlier.

In order to understand how the ripple is drawn it would be nice to slow the animation down. Unfortunately in the Developer Preview ripple animations do not appear to recognise the animation scaling factors under the developer options for the device. However, the ripple animation runs slower when we perform a long press, so we can use that to see exactly what’s happening:

The animation that runs fades in a large oval which completely fills the parent layout, and then grow a smaller circle from the point at which the user touched. One thing worth noting, however, is that the larger circle is not constrained to the bounds of its parent layout, its width and height exceed that of the parent – from the video you can clearly see that the outer circle overlaps the bottom layout (hopefully the reason for including the second layout is now obvious).

In the next article in this series we’ll look a bit deeper, and investigate some methods of constraining the ripple within its parent layout.

The source code for this article is available here.

© 2014, Mark Allison. All rights reserved.

Copyright © 2014 Styling Android. All Rights Reserved.
Information about how to reuse or republish this work may be available at http://blog.stylingandroid.com/license-information.

3 Comments

Leave a Reply

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.