223

I'm writing a few custom views which share some same-named attributes. In their respective <declare-styleable> section in attrs.xml I'd like to use the same names for attributes:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="MyView1">
        <attr name="myattr1" format="string" />
        <attr name="myattr2" format="dimension" />
        ...
    </declare-styleable>

    <declare-styleable name="MyView2">
        <attr name="myattr1" format="string" />
        <attr name="myattr2" format="dimension" />
        ...
    </declare-styleable>
</resources>

I'm getting an error saying that myattr1 and myattr2 are already defined. I found that I should omit the format attribute for myattr1 and myattr2 in MyView2, but if I do that, I obtain the following error in the console:

[2010-12-13 23:53:11 - MyProject] ERROR: In <declare-styleable> MyView2, unable to find attribute 

Is there a way I could accomplish this, maybe some sort of namespacing (just guessing)?

Suragch
  • 484,302
  • 314
  • 1,365
  • 1,393
Venator85
  • 10,245
  • 7
  • 42
  • 57

5 Answers5

465

Solution: Simply extract common attributes from both views and add them directly as children of the <resources> node:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <attr name="myattr1" format="string" />
    <attr name="myattr2" format="dimension" />

    <declare-styleable name="MyView1">
        <attr name="myattr1" />
        <attr name="myattr2" />
        ...
    </declare-styleable>

    <declare-styleable name="MyView2">
        <attr name="myattr1" />
        <attr name="myattr2" />
        ...
    </declare-styleable>
</resources>
Flow
  • 23,572
  • 15
  • 99
  • 156
Venator85
  • 10,245
  • 7
  • 42
  • 57
  • 17
    what happens when `myattr1` is string in `MyView1` and integer in `MyView2`? – foxx1337 Feb 01 '13 at 16:27
  • 2
    I don't think you can, but even if you could, I think these attribute names were a little misnamed :) – Venator85 Feb 02 '13 at 09:04
  • 4
    I don't think so, e.g. we can have 'orientation' attribute and for some view it is 'horizontal'/'vertical' and for other 'landscape'/'portrait'/'square'. From my point of view it's a bug (or at least inconsistent behavior) in Android (keep in mind that: 1. stylable attributes always start with prefix=view name and 2. if you create separate library projects for such views everything will work fine) – se.solovyev Apr 04 '13 at 09:45
  • Excellent, solved my `Execution failed for task ':mergeDebugResources'. res/values/attrs.xml: Error: Found item Attr/myAttr more than one time`. (Added to make this question better findable by Gradle users. :]) – Jonik Jan 15 '14 at 10:38
  • 5
    When I follow this answer I get `ERROR: In com_app_view_widget, unable to find attribute customAttr` For all the view I try to declare for. Any ideas? – M Dapp Jun 09 '14 at 14:54
  • 3
    @Dapp cleaned and rebuilt the project and that error message gone too. – mehmet6parmak Jan 10 '15 at 09:45
  • 65
    @Google: Crappy design – Glenn Bech Mar 23 '15 at 15:34
  • 7
    @foxx1337 Just use ``. Works for me. – Mygod Aug 18 '15 at 06:43
  • 2
    @Mygod Doing that you are losing xml field type validation by accepting 2 types when only 1 is supposed to be allowed. – GuillermoMP Jan 27 '17 at 10:43
  • 4
    @GlennBech exactly. Why we even need to declare stylable of a CustomView and put the possible attributes inside it, if it still can conflicted with other same-named attr under different stylables. Google seems overcomplicate things all over android development. – Moses Aprico Apr 27 '17 at 11:31
  • This does cause an extra abstraction layer. On the other hand, it seems to help with consistent naming of attrs. When using lots of custom widgets, it's actually a nice feature. Subtle naming differences are hard to deal with when using a lot of custom attrs. E.g., picking between string_res_id or text_res_id when adding a custom view to a layout is frustrating. – methodsignature Dec 04 '18 at 13:11
  • At first, this answer seemed not working but when I rebuilt the project, Tada! it is working like a charm. – Emad Razavi Jul 02 '20 at 06:29
  • why is this the accepted answer if it can't be referenced via code i.e. `R.styleable.???_myattr1` or `R.styleable.???_myattr2` – linker Dec 24 '20 at 18:11
  • i cannot get value from attr xml, why does everyone voted it? – Tuan Dao Nov 30 '21 at 15:33
71

I am posting this answer as the above-posted solution didn't work out for me in Android Studio. I need to share my custom attributes among my custom views so I tried the above solution in Android Studio but had no luck. So I experiment and go a way to do it. Hope it might help someone looking for the same problem.

  <?xml version="1.0" encoding="utf-8"?>
    <resources>
    <!-- parent styleable -->
     <declare-styleable name="MyView">
         <attr name="myattr1" format="string" />
         <attr name="myattr2" format="dimension" />
     </declare-styleable>

     <!-- inheriting parent styleable -->
     <!-- also note "myBackgroundColor" belongs to child styleable"MyView1"-->
    <declare-styleable name="MyView1" parent="MyView">
        <attr name="myattr1" />
        <attr name="myattr2" />
        <attr name="myBackgroundColor" format="color"/>
    </declare-styleable>


    <!-- inheriting parent styleable -->
    <!-- same way here "myfonnt" belongs to child styelable "MyView2" -->
    <declare-styleable name="MyView2" parent="MyView">
        <attr name="myattr1" />
        <attr name="myattr2" />
        <attr name="myfont" format="string"/>
        ...
    </declare-styleable>
</resources>

This works for me completely. We need to make a Parent styleable and then we need to inherit that parent styleable. For example, as I have done above : Parent styleable name MyView and inherited this to my other styleable like MyView1 and MyView2 respectively.

Evin1_
  • 12,292
  • 9
  • 45
  • 47
Priya Singhal
  • 1,261
  • 11
  • 16
  • 2
    This worked for me. I couldn't find a way to reference the extracted attributes in code from the accepted answer. – Nemanja Kovacevic Oct 14 '16 at 00:47
  • Hmm..that's weird.. the accepted solution seems to be working well for me (targetSdkVersion 27). Maybe because attributes like "text" is common & may have existed in some other attrs.xml..? – Aba Jun 11 '18 at 02:10
  • I tried with a name that's most probably uncommon & the accepted solution still worked for me. – Aba Jun 11 '18 at 02:20
  • I agree with the first comment! There is no way to reference an extracted attr from typedArray. Therefore you need to define a styleable parent – reavcn Jul 11 '19 at 15:23
  • for this to work don't forget to address this attribute in parent and not in child (for class `MyView2` right: `R.styleable.MyView2_myattr1`, wrong: `R.styleable.MyView_myattr1`) – vigilancer Jan 29 '20 at 21:31
  • 1
    and you don't really need to specify `parent` at all for this to work – vigilancer Jan 29 '20 at 21:33
33

As Priya Singhal answered, Android Studio requires the common attribute names to be defined within their own style name. They can't be at the root any more.

However, there are a couple other things to note (which is why I am also adding an answer):

  • The common styles don't need to be named the same thing as a view. (Thanks to this answer for pointing that out.)
  • You don't need to use inheritance with a parent.

Example

Here is what I did in a recent project that has two custom views that both share the same attributes. As long as the custom views still have the names for the attributes and don't include a format, I can still access them as normal from code.

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

    <!-- common attributes to all custom text based views -->

    <declare-styleable name="TextAttributes">
        <attr name="text" format="string"/>
        <attr name="textSize" format="dimension"/>
        <attr name="textColor" format="color"/>
        <attr name="gravity">
            <flag name="top" value="48" />
            <flag name="center" value="17" />
            <flag name="bottom" value="80" />
        </attr>
    </declare-styleable>

    <!-- custom text views -->

    <declare-styleable name="View1">
        <attr name="text"/>
        <attr name="textSize"/>
        <attr name="textColor"/>
        <attr name="gravity"/>
    </declare-styleable>

    <declare-styleable name="View2">
        <attr name="text"/>
        <attr name="textSize"/>
        <attr name="textColor"/>
        <attr name="gravity"/>
    </declare-styleable>

</resources>

Streamlined example

In fact, I don't even need to put the attributes under a custom name. As long as I define them (give them a format) for at least one custom view, I can use them anywhere (without the format). So this also works (and looks cleaner):

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

    <declare-styleable name="View1">
        <attr name="text" format="string"/>
        <attr name="textSize" format="dimension"/>
        <attr name="textColor" format="color"/>
        <attr name="gravity">
            <flag name="top" value="48" />
            <flag name="center" value="17" />
            <flag name="bottom" value="80" />
        </attr>
    </declare-styleable>

    <declare-styleable name="View2">
        <attr name="text"/>
        <attr name="textSize"/>
        <attr name="textColor"/>
        <attr name="gravity"/>
    </declare-styleable>

</resources>

For a large project, though, this could get messy and defining them at the top in a single location might be better (as recommended here).

Suragch
  • 484,302
  • 314
  • 1,365
  • 1,393
  • What's wrong with inheritance? I have custom view hierarchies who's associated styleables do not reflect this relationship, which can only be deduced in the associated style definitions by naming conventions and the specific items defined therein (noticing that a few belong to one styleable and a few to another). I'd rather make it explicit with the `parent` attribute but haven't seen many posts suggesting it's usage. – samus Jan 15 '19 at 21:11
  • @samis, I haven't worked on this for a while, but I don't know anything wrong with using `parent`. I think I was just saying it wasn't required. – Suragch Jan 15 '19 at 22:34
  • It's not required, I just made another subclass wrapped around an additional attribute that I didn't want to put into the base class. I'm just using comments and naming conventions to indicate the separation. – samus Jan 16 '19 at 13:06
  • The streamlined example seems to be what worked the best for me. Using `parent` just resulted in it returning the incorrect values for certain attributes. `` results in `getString(R.attr.MyView_label) == "monospace"` – Sollace Feb 09 '21 at 10:52
10

Thanks Lewis I had the same problem , and your inheritance solution gave me the hint for doing it like below and it works fine.I just declared common attributes at the above and rewrite it in the body of style declaration again without formatting. I hope it helps someone

<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- common attributes -->
     <attr name="myattr1" format="string" />
     <attr name="myattr2" format="dimension" />

 <!-- also note "myBackgroundColor" belongs to child styleable"MyView1"-->
<declare-styleable name="MyView1" >
    <attr name="myattr1" />
    <attr name="myattr2" />
    <attr name="myBackgroundColor" format="color"/>
</declare-styleable>

<!-- same way here "myfonnt" belongs to child styelable "MyView2" -->
<declare-styleable name="MyView2" parent="MyView">
    <attr name="myattr1" />
    <attr name="myattr2" />
    <attr name="myfont" format="string"/>
    ...
</declare-styleable>

Hanieh Variani
  • 101
  • 1
  • 5
1

Just in case someone still stuck with this problem after tried available solution. I stuck with add subtitle attribute with string format.

My solution is remove the format.

before:

<attr name="subtitle" format="string"/>

after:

<attr name="subtitle"/>

Ahmad Muzakki
  • 1,058
  • 12
  • 17