January 1, 2013

Android tricks: Using shape for theming

By

Android supports all sorts of “drawable” objects (the Bitmap drawable, which can contain images in formats like PNG and JPG, is one example). The one we are going to talk about today is so-called Shape drawable.

Shape drawables are defined with a simple XML syntax and stored in the drawable folder of an Android solution. Several basic shapes are supported, including rectangles (with optional rounded corners) and ovals. The border and fill of a shape can be specified—the user can choose a solid fill color or a multistep gradient. The Android designer in MonoDevelop is actually smart enough to interpret XML drawables, so you can change them and see the result appear live in MonoDevelop’s Android layout designer as you are building your application!

A standalone shape drawable is probably not very useful, but shapes can be immensely helpful when used together in conjunction with other Views. You can use a combination of simple shapes and other elements for simple widget theming. In the following image, which was first shown in my expandable ListView blog post, you can see three different shape drawables in action:

expandhelper-collapsed

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 following XML is the source code for that shape:

<?xml version="1.0" encoding="UTF-8" ?>
<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>

In the following XML layout, you can see the shape referenced in the 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 set the size of the FrameLayout accordingly. The necessary value will depend on your application. You can even set it dynamically, though that has to be done 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 one are a convenient way to add some eye candy to your UI elements. For more details, see the Android graphics talk (the relevant part starts at 3:10) that Google developers Chet Haase and Romain Guy presented at Devoxx.

The XML code for the bar shape is given below:

<?xml version="1.0" encoding="UTF-8" ?>
<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>

In this example, I combined the <solid/> element to paint my base color and the <gradient/> element to perform the shading on top of it.

Again, to apply the effect, simply use the android:background attribute in one of your layout elements:

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

Finally, the border (with round corners) that is displayed around the whole UI widget is also a shape drawable:

<?xml version="1.0" encoding="UTF-8" ?>
<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 this case, since we only care about the border, we set the filling color of the shape to be transparent. The <stroke/> element defines your border while the <corners/> element makes it round.

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

header-shape-gradient

The following code defines the Shape XML (notice the left padding value, which is used for indenting):

<?xml version="1.0" encoding="UTF-8" ?>
<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>

The following code defines the TextView XML (as always, the shape is 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!

TwitterFacebookGoogle+LinkedInEmail