Defining custom attrs Defining custom attrs android android

Defining custom attrs


Currently the best documentation is the source. You can take a look at it here (attrs.xml).

You can define attributes in the top <resources> element or inside of a <declare-styleable> element. If I'm going to use an attr in more than one place I put it in the root element. Note, all attributes share the same global namespace. That means that even if you create a new attribute inside of a <declare-styleable> element it can be used outside of it and you cannot create another attribute with the same name of a different type.

An <attr> element has two xml attributes name and format. name lets you call it something and this is how you end up referring to it in code, e.g., R.attr.my_attribute. The format attribute can have different values depending on the 'type' of attribute you want.

  • reference - if it references another resource id (e.g, "@color/my_color", "@layout/my_layout")
  • color
  • boolean
  • dimension
  • float
  • integer
  • string
  • fraction
  • enum - normally implicitly defined
  • flag - normally implicitly defined

You can set the format to multiple types by using |, e.g., format="reference|color".

enum attributes can be defined as follows:

<attr name="my_enum_attr">  <enum name="value1" value="1" />  <enum name="value2" value="2" /></attr>

flag attributes are similar except the values need to be defined so they can be bit ored together:

<attr name="my_flag_attr">  <flag name="fuzzy" value="0x01" />  <flag name="cold" value="0x02" /></attr>

In addition to attributes there is the <declare-styleable> element. This allows you to define attributes a custom view can use. You do this by specifying an <attr> element, if it was previously defined you do not specify the format. If you wish to reuse an android attr, for example, android:gravity, then you can do that in the name, as follows.

An example of a custom view <declare-styleable>:

<declare-styleable name="MyCustomView">  <attr name="my_custom_attribute" />  <attr name="android:gravity" /></declare-styleable>

When defining your custom attributes in XML on your custom view you need to do a few things. First you must declare a namespace to find your attributes. You do this on the root layout element. Normally there is only xmlns:android="http://schemas.android.com/apk/res/android". You must now also add xmlns:whatever="http://schemas.android.com/apk/res-auto".

Example:

<?xml version="1.0" encoding="utf-8"?><LinearLayout  xmlns:android="http://schemas.android.com/apk/res/android"  xmlns:whatever="http://schemas.android.com/apk/res-auto"  android:orientation="vertical"  android:layout_width="fill_parent"  android:layout_height="fill_parent">    <org.example.mypackage.MyCustomView      android:layout_width="fill_parent"      android:layout_height="wrap_content"      android:gravity="center"      whatever:my_custom_attribute="Hello, world!" /></LinearLayout>

Finally, to access that custom attribute you normally do so in the constructor of your custom view as follows.

public MyCustomView(Context context, AttributeSet attrs, int defStyle) {  super(context, attrs, defStyle);  TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MyCustomView, defStyle, 0);  String str = a.getString(R.styleable.MyCustomView_my_custom_attribute);  //do something with str  a.recycle();}

The end. :)


Qberticus's answer is good, but one useful detail is missing. If you are implementing these in a library replace:

xmlns:whatever="http://schemas.android.com/apk/res/org.example.mypackage"

with:

xmlns:whatever="http://schemas.android.com/apk/res-auto"

Otherwise the application that uses the library will have runtime errors.


The answer above covers everything in great detail, apart from a couple of things.

First, if there are no styles, then the (Context context, AttributeSet attrs) method signature will be used to instantiate the preference. In this case just use context.obtainStyledAttributes(attrs, R.styleable.MyCustomView) to get the TypedArray.

Secondly it does not cover how to deal with plaurals resources (quantity strings). These cannot be dealt with using TypedArray. Here is a code snippet from my SeekBarPreference that sets the summary of the preference formatting its value according to the value of the preference. If the xml for the preference sets android:summary to a text string or a string resouce the value of the preference is formatted into the string (it should have %d in it, to pick up the value). If android:summary is set to a plaurals resource, then that is used to format the result.

// Use your own name space if not using an android resource.final static private String ANDROID_NS =     "http://schemas.android.com/apk/res/android";private int pluralResource;private Resources resources;private String summary;public SeekBarPreference(Context context, AttributeSet attrs) {    // ...    TypedArray attributes = context.obtainStyledAttributes(        attrs, R.styleable.SeekBarPreference);    pluralResource =  attrs.getAttributeResourceValue(ANDROID_NS, "summary", 0);    if (pluralResource !=  0) {        if (! resources.getResourceTypeName(pluralResource).equals("plurals")) {            pluralResource = 0;        }    }    if (pluralResource ==  0) {        summary = attributes.getString(            R.styleable.SeekBarPreference_android_summary);    }    attributes.recycle();}@Overridepublic CharSequence getSummary() {    int value = getPersistedInt(defaultValue);    if (pluralResource != 0) {        return resources.getQuantityString(pluralResource, value, value);    }    return (summary == null) ? null : String.format(summary, value);}

  • This is just given as an example, however, if you want are tempted to set the summary on the preference screen, then you need to call notifyChanged() in the preference's onDialogClosed method.