Shadow effect with custom shapes

It’s easy to create a shadow for your views using custom shapes.

The idea it’s that you create the shadow layer as background first and the content layers on top of that.
But you also need to displace the layers in order to create the shadow effect.

For example:

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <layer-list>
            <item android:right="4dp" android:bottom="4dp">

                <!-- SHADOW LAYER -->
            </item>
            <item android:left="4dp" android:top="4dp">
                <!-- CONTENT LAYER-->
            </item>
        </layer-list>
    </item>
</selector>

You can control the shadow direction and size through the top, bottom, right and left parameters.

Shadow shapes

Here we have an example of our app WhoSaid:

Custom shadow shape

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

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_pressed="true">
        <layer-list>
            <item android:left="4dp" android:top="4dp">
                <shape>
                    <solid android:color="#ff58bb52" />
                    <corners android:radius="30dip"/>
                </shape>
            </item>
        </layer-list>
    </item>
    <item>
        <layer-list>
            <!-- SHADOW LAYER -->
            <item android:left="4dp" android:top="4dp">
                <shape>
                    <solid android:color="#66000000" />
                    <corners android:radius="30dip"/>
                </shape>
            </item>
            <!-- CONTENT LAYER -->
            <item android:bottom="4dp" android:right="4dp">
                <shape>
                    <solid android:color="#ff58bb52" />
                    <corners android:radius="30dip"/>
                </shape>
            </item>
        </layer-list>
    </item>
</selector>

In our case we take off the shadow when the button is pressed to give improve the feedback.

Don’t forget that you can play with distance and colors to get new effects. For example you can use a gradient for the shadow layer.

Underlining views with custom shapes, overdraw performance

After the post of underlining views and custom drawables in android some people ask me about performance. So let’s talk about it.

So after a little research about performance I found this post http://www.curious-creature.org/2012/12/01/android-performance-case-study/ talking about performance and overdraw. I recommend read the post, but in resume we refer to overdraw as the number of times the GPU has to draw the screen for each component. You can active it in the Developer options as Show GPU overdraw.

It will color your screen this way:

Wordpress overdraw

  • No color means there is no overdraw. The pixel was painted only once. In this example, you can see that the background is intact.
  • Blue indicates an overdraw of 1x. The pixel was painted twice. Large blue areas are acceptable (if the entire window is blue, you can get rid of one layer.)
  • Green indicates an overdraw of 2x. The pixel was painted three times. Medium-sized green areas are acceptable but you should try to optimize them away.
  • Light red indicates an overdraw of 3x. The pixel was painted four times. Small light red areas are acceptable.
  • Dark red indicates an overdraw of 4x or more. The pixel was painted 5 times or more. This is wrong. Fix it.

In the other post we put this drawable:

Layers 2

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

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_selected="true">
        <layer-list>
            <item>
                <shape>
                    <solid android:color="#ffff7a00" />
                </shape>
            </item>
            <item android:bottom="3dp">
                <shape>
                    <solid android:color="#222222" />
                </shape>
            </item>
        </layer-list>
    </item>
</selector>

And we can see it has a 2x overdraw (green color) because of the 2 layers:

2 x overdraw

But we can fix that by changing the drawable for not to overlap between the layers.

layers_no_overlap_1

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

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_selected="true">
        <layer-list>
            <item android:bottom="3dp">
                <shape>
                    <solid android:color="#222222" />
                </shape>
            </item>
            <item android:top="29dp">
                <shape >
                    <solid android:color="#ffff7a00" />
                </shape>
            </item>
        </layer-list>
    </item>
</selector>

Now we have a 1x (blue) overdraw:

1 Overdraw

Because now the layers do not overlap it doesn’t matter which layer goes on top of another.

The main drawback of this method is that you only can use if you know beforehand the height of the drawable.

If you don’t know the height you will have to use a 9patch button or keep it like we put in the previous post paying the overdraw cost.

In our case we would use the drawable method in development to be able to change the drawables fast, and change to 9patch button if needed.

Example with 3 layer

This drawable is compound of 3 layers (see this post if you want to see the xml).

3 Layers example

The overdraw of this view is 3x:

3xoverdraw

Changing the drawable we can reduce it to a 1x factor:

Layers no overlap 2

<item android:state_selected="true">
    <layer-list>
        <item android:top="29dp">
            <shape>
                <solid android:color="#222222" />
            </shape>

        </item>
        <item android:top="26dp" android:bottom="3dp">
            <shape >

                <solid android:color="#ffff7a00" />
            </shape>
        </item>
        <item android:bottom="6dp">
            <shape>
                <solid android:color="#222222" />
            </shape>
        </item>
    </layer-list>
</item>

With the new xml the overdraw remains 1x (blue).

New overdraw for 3 layers