Reading attribute values of custom components

Assuming you’ve defined a custom component which inherits from View and it’s called MyComponent, create a values/attrs.xml file and add a line for each attribute you want to allow in your component. The file should look more or less like this:

<?xml version="1.0" encoding="utf-8"?>
<resources>
        <declare-styleable name="MyComponent">
                <attr name="android:padding" />
                <!-- You can add as many attributes as required -->
        </declare-styleable>
</resources>

If you also want to add custom attributes, you need to define a namespace in the layout file that uses components with custom attributes. Something like this:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:myNamespace="http://schemas.android.com/apk/res/com.mycompany.projectname"
>
<!-- stuff -->
</LinearLayout>

And in values/attrs.xml you’ll use myNamespace: instead of android: when listing the supported attributes:

<?xml version="1.0" encoding="utf-8"?>
<resources>
        <declare-styleable name="MyComponent">
                <attr name="myNamespace:esotericAttribute" />
        </declare-styleable>
</resources>

Of course both can be mixed!

<?xml version="1.0" encoding="utf-8"?>
<resources>
        <declare-styleable name="MyComponent">
                <attr name="android:padding" />
                <attr name="myNamespace:esotericAttribute" />
        </declare-styleable>
</resources>

Then in your layout file declare your components and required attributes as usual:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:myNamespace="http://schemas.android.com/apk/res/com.mycompany.projectname"
>
        <com.mycompany.projectname.MyComponent
                android:padding="10dip"
                myNamespace:esotericAttribute="Always"
        />
</LinearLayout>

We’ll read their values in the constructor which accepts Context and AttributeSet as parameters:

public MyComponent(Context context, AttributeSet attrs)
{
        super(context, attrs);
        int padding;
        String esotericAttribute;

        TypedArray theAttrs = context.obtainStyledAttributes(attrs, R.styleable.MyButton);

        // Gets the value "as is", with no further conversions
        esotericAttribute = theAttrs.getString(R.styleable.MyComponent_myNamespace_esotericAttribute);

        // Scaled "device independent" value, 0 is the default if attribute is not specified
        padding = theAttrs.getDimensionPixelSize(R.styleable.MyComponent_android_padding, 0);

        // call this when you're done with the attributes
        theAttrs.recycle();

        // now you would do stuff with the read attribute values        ...
}

A note on getDimensionPixelSize: yes, you could be “brave” and use getString instead of getDimensionPixel Size, but you’d also have to parse the values and make sure both the values and the units are right, and take care of scaling the readings so that they would be appropriate for the display size in which the application is running.

But it’s evident that it’s way more comfortable to let getDimensionPixelSize do all that work for you!

For example, in an HTC Evo 4G, which is “high density”, getDimensionPixelSize for a padding of 10dip returns 15 pixels = 1.5 * 10, where 1.5 is the display density.

References and more information: