XML Attributes for Custom Views and Layouts in Android OS SDK

One of the nifty features of the Android OS SDK is the ability to add custom components to your layouts. This lets you alter the behaviour of existing components or create your very own specialized components.

Typically you would extend any of the existing Android views or layouts and override various methods to achieve whatever you want to do:

package com.example;

import android.content.Context;
import android.util.AttributeSet;
import android.widget.LinearLayout;

public class MyLayout extends LinearLayout {

	public MyLayout(Context context) {
		super(context);
	}

	public MyLayout(Context context, AttributeSet attrs) {
		super(context, attrs);
	}

	@Override
	public void onFinishInflate() {
		super.onFinishInflate();

		// Your fancy code...
		
	}
	
}

Then in your layout XML files you can use these new objects by specifying the class path in the XML node:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 
	xmlns:android="http://schemas.android.com/apk/res/android"
	android:id="@+id/root"
	android:layout_width="fill_parent"
	android:layout_height="fill_parent"
	android:background="#fff">
	
	<com.example.MyLayout
		android:orientation="vertical"
		android:layout_width="fill_parent"
		android:layout_height="wrap_content" />

</RelativeLayout>

I’ve done this several times in my Android projects but I didn’t realize until recently that you can also specify your own attributes. To do so, you list what attributes are available in the attrs.xml file. For example, let’s say we want to create a text attribute for the MyLayout class. In res/values/attrs.xml you declare text as a string attribute that is styleable for the MyLayout class:

<?xml version="1.0" encoding="utf-8"?>
<resources>
     <declare-styleable name="MyLayout">
        <attr name="text" format="string" />
    </declare-styleable>
</resources>

Now you can add a new namespace (for example myapp) to your layout XML file and declare your own properties using that namespace:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 
	xmlns:android="http://schemas.android.com/apk/res/android"
	xmlns:myapp="http://schemas.android.com/apk/res/com.example"
	android:id="@+id/root"
	android:layout_width="fill_parent"
	android:layout_height="fill_parent"
	android:background="#fff">
	
	<com.example.MyLayout
		android:orientation="vertical"
		android:layout_width="fill_parent"
		android:layout_height="wrap_content" 
		myapp:text="My Special Text String"/>

</RelativeLayout>

All that’s left to do is read and apply the attributes when your custom component is created in MyLayout(Context context, AttributeSet attrs):

package com.example;

import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.widget.LinearLayout;
import com.example.R;

public class MyLayout extends LinearLayout {

	private String mText;

	public MyLayout(Context context) {
		super(context);
	}

	public MyLayout(Context context, AttributeSet attrs) {
	    super(context, attrs);
	
	    TypedArray a = context.obtainStyledAttributes(attrs,
	            R.styleable.MyLayout);
	    CharSequence s = a.getString(R.styleable.MyLayout_text);
	    if (s != null) {
	        mText = s.toString();
	    }
	    a.recycle();
	}

	@Override
	public void onFinishInflate() {
		super.onFinishInflate();

		// Your fancy code suing the mText property...
		
	}	
}

This ability to customize your object from the layout XML files comes in especially handy when providing different layouts for different devices or orientations.