Tuesday, March 29, 2011

Creating a reusable layout component in Android

Let us say that we want to use a common layout in several places in your user interface. Something like a box with a label and value text view vertically layed out. The application uses the same label/value TextView in 3 different places.

This is the common label/value layout component (file: layout/dashboard_item.xml):
<LinearLayout 
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="vertical"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent">
  
     <TextView android:id="@+id/label"
         android:layout_width="fill_parent"
         android:layout_height="wrap_content"
         android:layout_weight="0"
         android:gravity="center_horizontal"
         style="@style/label"
     />
     <TextView android:id="@+id/value"
         android:layout_width="fill_parent"
         android:layout_height="fill_parent"
         android:layout_weight="1"
         android:gravity="center"
         style="@style/output"
     />
</LinearLayout>
We want to use it in a screen definition (file: layout/home.xml):
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:labelValue="http://schemas.android.com/apk/res/net.kazed.sailor"
      android:layout_width="fill_parent"
      android:layout_height="fill_parent"
      android:orientation="vertical"
      style="@style/screen"
      >

    <net.kazed.sailor.view.LabelValueItem android:id="@+id/vmg"
       android:layout_width="fill_parent"
       android:layout_height="fill_parent"
       android:layout_weight="1"
       labelValue:label="@string/vmg"
    />
    
   </LinearLayout>
</RelativeLayout>
Here we reuse the label/value custom component implemented by the LabelValueItem class. This component has one custom attribute: "labelValue:label". Notice that this attribute has a namespace that corresponds with the package name of the Android application (defined in AndroidManifest.xml). The component class retrieves the value of the custom attribute and uses it to set the text of the label TextView (file: src/net/kazed/sailor/view/LabelValueItem.java):
public class LabelValueItem extends LinearLayout {
   
   public LabelValueItem(final Context context, final AttributeSet attrs) {
      super(context, attrs);
      LayoutInflater.from(context).inflate(R.layout.dashboard_item, this, true);

      TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.labelValue, 0, 0);

      TextView label = (TextView) findViewById(R.id.label);
      String labelText = array.getString(R.styleable.labelValue_label);
      if (labelText != null) {
         label.setText(labelText);
      }

      array.recycle();
   }

}
To make it all work, the application must define the custom attribute (file: values/attr.xml):
<?xml version="1.0" encoding="utf-8"?>
<resources>
   <declare-styleable name="labelValue">
      <attr name="label" format="string" />
   </declare-styleable>
</resources>