Good designers tend to follow general design trends in the market, whilst adding their own, special touch to designs. This is great, and creates some of the beautiful and unique apps we have on the Play Store and app markets today. However, it can also cause some headaches for us on the engineering end of things.

It needs to ripple, have rounded corners… oh, and three borders…

Yeah, not the biggest deal, but it can be tough to find a quick StackOverflow solution to this one. Most of the time design calls for a rippled button, a rounded button, a bordered button, or a combination of two of these. Lately, however, general design trends are tending towards things like this:

Credit: Postmates Merchant app

Here we have rounded, bordered corners. You can’t see it in this picture, but when you press on these cells, they ripple and fill with a ripple color. There are also a few different types of cells. Ones with borders on the top and sides, bottom and sides, and ones with borders only on the sides. Furthermore, when we press and hold, the ripple should fill up the view and abide by the rounded corner radius. Simply rippling the background of a rounded drawable will fill it in as a rectangle, such that the view’s ripple layer will show as a rectangle over your beautifully rounded corners.

We don’t want that.

Solution, answer, TLDR;

If you’re on API 21+, you can create a simple drawable. The idea is similar to that of a layer-list , or selectors if you’ve dealt with drawable states in the past. For views that have rounded corners on the top right, and top left sides, and are bordered at the top and sides, i.e. the Printer cell above, here is what the background layout would look like:

<?xml version="1.0" encoding="utf-8"?>

<ripple xmlns:android="http://schemas.android.com/apk/res/android"

android:color="@color/background_ripple">

<item>

<shape android:shape="rectangle">

<stroke

android:width="@dimen/divider_thickness"

android:color="@color/divider_default" />

<corners

android:bottomLeftRadius="0dp"

android:bottomRightRadius="0dp"

android:topLeftRadius="@dimen/corner_radius"

android:topRightRadius="@dimen/corner_radius"/>

</shape>

</item>



<item

android:left="@dimen/divider_thickness"

android:right="@dimen/divider_thickness"

android:top="@dimen/divider_thickness">

<shape android:shape="rectangle">

<solid android:color="@color/background_light" />

<corners

android:bottomLeftRadius="0dp"

android:bottomRightRadius="0dp"

android:topLeftRadius="@dimen/corner_radius"

android:topRightRadius="@dimen/corner_radius" />

</shape>

</item>

</ripple>

The basic idea here is that you have two layers: a thin border at the bottom, which creates a circle around the perimeter of the shape, and another layer on the top that fills the view, which is the color you expect to see as the background of the view you’re setting this on. On the second item (top layer), I am setting, essentially, a margin on the left , top , and right to reveal the bottom layer. This is how you create the borders: by adding margins to the top layer to reveal which sides you want bordered from the bottom layer.

If you want rounded corners, you can define which corners you want rounded, and with what radius. Make sure you do this on both layers, however.

You can use this principle to create combinations of bordered/rounded corners of every type. In our picture above, we used three different layouts. The one pasted above, for the top bordered cell, a similar one for the bottom bordered cell, and this one for the cells that are only bordered on the sides:

<?xml version="1.0" encoding="utf-8"?>

<ripple xmlns:android="http://schemas.android.com/apk/res/android"

android:color="@color/background_ripple">

<item>

<shape android:shape="rectangle">

<stroke

android:width="@dimen/divider_thickness"

android:color="@color/divider_default" />

</shape>

</item>



<item

android:left="@dimen/divider_thickness"

android:right="@dimen/divider_thickness">

<shape android:shape="rectangle">

<solid android:color="@color/background_light" />

</shape>

</item>

</ripple>

Since this view doesn’t require rounded corners, they don’t need to exist in the layout, since the default shape is a rectangle.

Summary

Designs can be appear complex at times, but that doesn’t mean the code/xml has to be. In our case, we used the simple idea of creating two layers, and adding margins on sides we wish to reveal the bottom layer of.

These drawables produce the background you would set on your views, whether they are a TextView , ConstraintLayout , Button , or a custom view.

Happy coding!