Yet Another [à compléter]

MfA Tricks #5 - Using Shape for Theming

Android supports all sort of drawable (Bitmap like PNG or JPG being only one of them). The one we are going to talk about today is so-called Shape drawable.

They can be defined with a simple XML syntax and support a number of basic shapes like rectangles (with rounded corners or not) or ovals which can be filled with a solid color or a gradient (borders can also be defined).

You define those in the drawable folder of your solution as normal. The Android designer in MonoDevelop is actually smart enough to pick up these ones so you can change them and see the result live while designing your application(!).

As a standalone drawable, it’s probably not that useful but used in conjunction with your Views, it allows you to simply theme your widgets.

In the following image reused from a previous post, we actually have 3 different shape drawables in action:

First, the circle around the coffee cup is an oval drawable filled with a semi-transparent color to darken the main header background color. The shape source is following:

<?xml version="1.0" encoding="UTF-8" ?>
<!-- aitemring.xml -->
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
    <solid android:color="#42000000" />
    <padding android:left="8dp" android:top="8dp"
             android:right="8dp" android:bottom="8dp" />
</shape>

The layout XML references it in the following snippet (android:background attribute):

<FrameLayout
   android:layout_width="80dp"
   android:layout_height="80dp"
   android:layout_gravity="center"
   android:background="@drawable/aitemring">
   <ImageView
       android:src="@drawable/coffee"
       android:layout_width="fill_parent"
       android:layout_height="fill_parent"
       android:scaleType="center" />
</FrameLayout>

To force the oval shape to be square, you have to force the size of the FrameLayout. The value depends on your application, you can even do it dynamically but it has to be in code.

Second, the bottom bar where the location and date appear is a rectangle shape filled with a gradient to give it volume.

Gradients like that are a cheap way to give eye candy to your UI elements (see @chethaase and @romainguy Devoxx talk from 03:10 for more on that).

The bar shape XML is given below:

<?xml version="1.0" encoding="UTF-8" ?>
<!-- aitemfooter.xml-->
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
    <solid android:color="#AA212121" />
    <gradient android:angle="90"
    	android:startColor="#59AAAAAA"
    	android:endColor="#59EEEEEC" />
    <padding android:left="8dp" android:top="8dp"
             android:right="8dp" android:bottom="8dp" />
</shape>

Here I’m combining the <solid/> element to paint my base color and the <gradient/> element to do the shading on top of it.

Again, to apply the effect, simply use the android:background attribute on your element:

<RelativeLayout
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="@drawable/aitemfooter">

Finally, the overall round-corner border around the whole UI widget is also given by a shape drawable:

<?xml version="1.0" encoding="UTF-8" ?>
<!-- aitemborder.xml -->
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
    <solid android:color="#00FFFFFF" />
    <stroke android:width="1dp" android:color="#ffaaaaaa" />
    <corners android:radius="3px" />
</shape>

In that case, since we only care about borders, we set the filling color of the shape to be transparent. The <stroke/> element define your borders while the <corners/> element make them rounded.

As a final treat, if you combine things, you can also make fancy header using shape:

Shape XML (notice the padding left value for indenting):

<?xml version="1.0" encoding="UTF-8" ?>
<!-- header.xml -->
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
    <solid android:color="#AA212121" />
    <gradient android:angle="-90"
    	android:startColor="#447F7F7F"
    	android:endColor="#007F7F7F" />
    <stroke android:width="1dp" android:color="#447F7F7F" />
    <padding android:left="48dp" android:top="8dp"
             android:right="8dp" android:bottom="8dp" />
</shape>

TextView XML (as always, shape registered in android:background attribute):

<TextView
    android:text="What"
    android:textAppearance="?android:attr/textAppearanceLarge"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:background="@drawable/header" />

Now go add a bit of “woowoo” to your app!