Michael Evans

A bunch of technobabble.

Android Ripples With Rounded Corners

| Comments

So recently I was trying to add a ripple to a view that had rounded corners. Simple enough right? Let’s just say I have a FrameLayout with the background similar to this one:

1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <solid android:color="@android:color/transparent"/>
    <corners android:radius="15dp" />
    <stroke
        android:width="1px"
        android:color="#000000" />
</shape>

It’s not quite as simple as setting the foreground to ?attr/selectableItemBackground, or else you’ll see the ripple surpasses the corners (which doesn’t look so bad when your border radius is small, but this would look terrible with a circlular view):

The solution for this lies in the special mask layer of the RippleDrawable. You specify the mask layer via the android:id value set to @android:id/mask. For the example above, you can set the mask to the same size/shape as the view you’re masking, and then the ripple will only show for that area. For something like our example above, you’d use something like this:

1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
    android:color="?android:attr/colorControlHighlight">
    <item android:id="@android:id/mask">
        <shape android:shape="rectangle">
            <solid android:color="#000000" />
            <corners android:radius="15dp" />
        </shape>
    </item>
    <item android:drawable="@drawable/rounded_corners" />
</ripple>

Now when you tap on the view, you’ll see something like this:

Huzzah!

Another tip: if you don’t set a click listener for a FrameLayout (like we used in this example), the pressed state will never be used!

Comments