自定义视图中attrs.xml中同名属性的处理方法

224

我正在编写一些自定义视图,它们共享一些同名属性。在它们各自的<declare-styleable>部分中的attrs.xml文件中,我想要使用相同的属性名称:

<?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>

我得到了一个错误,提示说myattr1myattr2已经被定义。我发现在MyView2中应该省略myattr1myattr2format属性,但是如果我这样做,我会在控制台上获得以下错误:

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

有没有一种方法可以实现这个,或者说可能采用某种命名空间的方式(只是猜测)?

5个回答

466

解决方案:只需从两个视图中提取共同的属性,并将它们直接作为<resources>节点的子元素添加即可:

<?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>

17
MyView1 中的 myattr1 是字符串而 MyView2 中是整数时会发生什么? - foxx1337
4
我不这么认为,例如我们可以有“orientation”属性,对于某些视图它是“水平/垂直”的,对于其他视图则是“横向/纵向/正方形”的。从我的角度来看,这是Android中的一个错误(或者至少是不一致的行为)。请注意:1.可样式化的属性始终以前缀=视图名称开头;2.如果您为此类视图创建单独的库项目,则一切都将正常工作。 - se.solovyev
5
当我按照这个答案操作时,无论我尝试声明哪个视图,都会出现以下错误信息:ERROR: In <declare-styleable> com_app_view_widget, unable to find attribute customAttr有任何想法吗? - M Dapp
65
谷歌:糟糕的设计。 - Glenn Bech
7
只需使用<attr name="myattr1" format="string|integer" />。这对我有效。 - Mygod
显示剩余9条评论

73

我发表这篇答案是因为之前给出的解决方案在Android Studio上对我无效。我需要在我的自定义视图之间共享自定义属性,所以我尝试了以上的解决方案,但没有成功。因此,我进行了实验并找到了一种方法来解决这个问题。希望能够帮助到寻找同样问题答案的人。

  <?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>

这对我完全有效。 我们需要使一个父样式可层叠,并且我们需要继承该父样式。例如,就像我上面所做的那样: 父样式名称为MyView,并将其继承到我的其他样式中,例如MyView1和MyView2。


2
这对我有用。我找不到一种方法来从被接受的答案中引用代码中提取的属性。 - Nemanja Kovacevic
嗯...这很奇怪...被接受的解决方案对我来说似乎运作良好(targetSdkVersion 27)。也许是因为像“text”这样的属性很普通,可能已经存在于其他的attrs.xml文件中..? - Aba
我尝试了一个可能不太常见的名称,但被接受的解决方案仍然适用于我。 - Aba
我同意第一条评论!无法从typedArray中引用提取的属性。因此,您需要定义一个styleable父项。 - reavcn
为了使这个工作正常,请不要在子级中处理这个属性,而是在父级中处理 (对于类 MyView2 正确的方式是 R.styleable.MyView2_myattr1,错误的方式是 R.styleable.MyView_myattr1)。 - vigilancer
1
你其实不需要指定“parent”就可以让它工作。 - vigilancer

33

正如Priya Singhal所回答的那样,Android Studio要求通用属性名称在其自己的样式名称中定义,不能再放在根目录下。

然而,还有几件事情需要注意(这就是为什么我也要添加一个答案的原因):

  • 通用样式不需要与视图命名相同。(感谢这个答案指出了这一点。)
  • 你不需要使用带父级的继承。

示例

以下是我在最近一个项目中所做的事情,其中有两个自定义视图都共享相同的属性。只要自定义视图仍然具有属性名称,并且不包括format,我仍然可以像往常一样从代码中访问它们。

<?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>

简化示例

实际上,我甚至不需要将属性放在自定义名称下。只要为它们定义(给它们一个 format )至少一个自定义视图,我就可以在任何地方使用它们(不需要 format )。因此,这也有效且更加简洁:

<?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>

对于一个大项目而言,这样做可能会变得混乱,因此在顶部的单个位置中定义它们可能更好(如这里所推荐)。


继承有什么问题吗?我有自定义视图层次结构,其关联的可样式化属性不反映此关系,只能通过命名约定和其中定义的特定项目在相关样式定义中推导出来(注意到一些属于一个可样式化属性,而另一些属于另一个)。我宁愿用parent属性明确表示它,但没有看到很多帖子建议使用它。 - samus
@samis,我已经有一段时间没有处理这个了,但是我不知道使用“parent”有什么问题。我想我只是在说它不是必需的。 - Suragch
不是必需的,我只是创建了另一个子类,包装了一个我不想放入基类中的附加属性。我只是使用注释和命名约定来指示分离。 - samus
经过简化的示例似乎对我来说是最有效的。使用 parent 只会导致某些属性返回不正确的值。<MyView app:label="label" app:font="monospace"> 会导致 getString(R.attr.MyView_label) == "monospace" - Sollace

10

谢谢Lewis,我遇到了同样的问题,你的继承解决方案给了我做以下如下的提示,它可以很好地工作。在上面声明公共属性,并在样式声明的主体中再次重写,而不进行格式化。我希望它能帮助别人。

<?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>


1

如果有人在尝试了现有解决方案后仍然遇到这个问题,请注意。我遇到了添加subtitle属性时使用了string格式的困境。

我的解决方案是删除格式。

之前:

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

之后:

<attr name="subtitle"/>


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接