8

Suppose I am making some new views with styleable attributes. I declare them thusly (this is how the documentation says to do it:

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <declare-styleable name="TriangleView">
        <attr name="direction">
            <enum name="NE" value="0" />
            <enum name="NW" value="1" />
            <enum name="SW" value="2" />
            <enum name="SE" value="3" />
        </attr>
    </declare-styleable>

    <declare-styleable name="BannerView">
        <attr name="direction">
            <enum name="NE" value="0" />
            <enum name="NW" value="1" />
            <enum name="SW" value="2" />
            <enum name="SE" value="3" />
        </attr>
        <attr name="thickness" format="dimension" />
    </declare-styleable>
</resources>

However, this won't work because all attributes are apparently in the same namespace, and I get the error Error: Attribute "direction" has already been defined.

So apparently I have to move the apparently duplicated attributes outside the <declare-styleable> like this:

<?xml version="1.0" encoding="utf-8"?>
<resources>


    <attr name="direction">
        <enum name="NE" value="0" />
        <enum name="NW" value="1" />
        <enum name="SW" value="2" />
        <enum name="SE" value="3" />
    </attr>

    <declare-styleable name="BannerView">
        <attr name="thickness" format="dimension" />
    </declare-styleable>
</resources>

But this poses two questions:

  1. If this works, what exactly is the point of <declare-styleable>?
  2. What if I want the attribute to behave differently in different views? For example if BannerView's direction can only be up or down.
Timmmm
  • 88,195
  • 71
  • 364
  • 509

1 Answers1

16

What exactly is the point of <declare-styleable>?

<declare-stylable> tags let you declare attributes for your custom views that you can then set for those views in xml. There are really 3 parts to using the attribute:

  1. Declare an <attr> inside of a <declare-stylable> tag.
  2. Define a custom namespace in your xml layout pointing to your app package name (ex. app). Use the custom attribute in your layout (ex. app:direction="NW").
  3. In your custom view, override the constructors with an AttributeSet parameter, get a TypedArray and read the custom attributes, if any, from it and then within the constructor tell the view how to use those attributes appropriately.

What if I want the attribute to behave differently in different views? For example if BannerView's direction can only be up or down.

Try something like this:

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <attr name="direction">
        <enum name="NE" value="0" />
        <enum name="NW" value="1" />
        <enum name="SW" value="2" />
        <enum name="SE" value="3" />
    </attr>

    <declare-styleable name="TriangleView">
        <attr name="direction" />
    </declare-styleable>

    <declare-styleable name="BannerView">
        <attr name="direction" />
        <attr name="thickness" format="dimension" />
    </declare-styleable>
</resources>

When you build your xml layout for TriangleView or BannerView, you can use the app:direction="NW" example for both. In the constructors with AttributeSet in TriangleView or BannerView, the attributes will have the same format as the original, but what you do with that value is dependent on your implementation of the constructors in each respective view (can be the same or different for both).

If you want attributes to be defined differenly (ie. different "format" or "enum") for different views, then you have to create different attributes with different names.

happydude
  • 3,869
  • 2
  • 23
  • 41
  • Awesome, actually helped me as well for attr defined in the app-compat lib! So it's basically, if an attr is already defined, it should be used in the declare-styleable, without trying to override the format. Cool cool cool. – Redwarp Jan 05 '16 at 16:22
  • But in layout gradle insists you use 'xmlns:XXX="http://schemas.android.com/apk/res-auto"' for declaring namespace so even if you use for instance two namespaces: 'xmlns:XXX" and 'xmlns:XXY" and have attributes with and and declare attributes for a view in a layout file as XXX:a=..." and XXY:a="..." you have a namespace clash and get a "Duplicate resources" error. Why? – steven smith Mar 10 '21 at 17:04