如何从可绘制对象引用样式属性?


96

我想为我的应用程序选择2个主题。为了做到这一点,我定义了一些属性,如下所示:

 <attr format="color" name="item_background" />

然后,我创建了两个主题,如下所示:

  <style name="ThemeA">
     <item name="item_background">#123456</item>
 </style>

 <style name="ThemeB">
     <item name="item_background">#ABCDEF</item>
 </style>

这种方法效果很好,使我可以轻松创建和修改多个主题。问题在于,它似乎只能在Views中使用,而不能在Drawables中使用

例如,从布局内的View引用值可以工作:

 <TextView android:background="?item_background" />

但是在Drawable中执行相同操作不会:

 <shape android:shape="rectangle">
     <solid android:color="?item_background" />
 </shape>

运行应用程序时出现此错误:

    java.lang.UnsupportedOperationException: Can't convert to color: type=0x2

如果不是?item_background我使用硬编码颜色,而是可以使用它,但是那不允许我使用主题。我也尝试过?attr:item_background,但同样发生。

我该怎么办?为什么在Views中却不能在Drawables中使用呢?我在文档的任何地方都找不到此限制...


:这可能是以下问题的重复stackoverflow.com/questions/7529574/...
保罗Lammertsma

@Martin M.,您对此有什么看法?
user123321 2011年

有什么解决办法吗?我碰到了完全一样的墙
Guillaume 2012年

另一个最近的问题在这里:stackoverflow.com/q/12115125/317889相同的问题。整理出谷歌。
HGPB 2012年

3
显然,此问题已在Android L预览版中解决,如此处指定:code.google.com/p/android/issues/detail?
id=26251

Answers:


161

以我的经验,不可能在XML可绘制对象中引用属性。
为了使您的主题,您需要:

  • 每个主题创建一个XML可绘制对象。
  • 使用@color标记或#RGB格式将所需颜色直接包含在可绘制图形中。

attrs.xml中为您的可绘制对象设置一个属性。

<?xml version="1.0" encoding="utf-8"?>
<resources>
   <!-- Attributes must be lowercase as we want to use them for drawables -->
   <attr name="my_drawable" format="reference" />
</resources>

将您的可绘制对象添加到theme.xml中

<style name="MyTheme" parent="@android:style/Theme.NoTitleBar">
   <item name="my_drawable">@drawable/my_drawable</item>
</style>

使用属性在布局中引用您的可绘制对象。

<TextView android:background="?my_drawable" />

20
似乎,如果您要保持兼容性,则仍然需要此解决方案...是的,L是固定的,主题颜色也得到了很好的引用,但是相同的代码在不同的pre-L设备上运行,而attr引用实际上不起作用前置L装置!大声笑..因此,对于我来说,如果我必须保持不同的可绘制对象不是固定的。
Davideas

2
我希望我能两次投票赞成。只是试图简化我的形状以使用引用而不是硬编码的#RGB,并得到了相同的错误。来到SO寻求解决方案,并找到了我两周前赞成的相同答案!(facepalm)
Tiksi

2
这是超级忍者!
MariusBudin '16

4
啊!这应该是Google进行backport修复的头等大事!?attr-reference表示法非常有用且重要,以至于如果一个应用程序中没有多个主题,它几乎是无法生存的。现在,我必须为每种自定义按钮类型制作三个可绘制的XML!
斯蒂芬·亨宁森

2
那就是我想要的。<21 SDK中非常简单且不错的解决方案。
Adnan Abdollah Zaki

20

lollipop(API 21)开始,支持此功能,请参阅 https://code.google.com/p/android/issues/detail?id=26251

但是,如果您要定位的设备没有棒棒糖,请不要使用它,因为它会崩溃,请改用已接受答案中的解决方法。


有机会使用某种支持库使其在pre L API上运行吗?
伊万2015年

2
不,支持库是一组可在较旧设备上使用的API,此行为更改在编译器和构建工具中,因此与支持库无关。
marmor 2015年

3
不,在棒棒糖中,这不是一项已实现的功能,而是一个明显的错误修复程序。还有一个严重的错误。
阿德里安·西卡鲁

该死的,我以为我有一个不错的主题解决方案。现在,我必须回去创建一堆按钮选择器可绘制对象。
filthy_wizard

4

虽然不可能在棒棒糖之前的设备上从可绘制对象中引用样式属性,但是对于颜色状态列表来说是可能的。您可以使用Android支持库中的AppCompatResources.getColorStateList(Context context,int resId)方法。缺点是您必须以编程方式设置这些颜色状态列表。

这是一个非常基本的例子。

color / my_color_state.xml

<selector xmlns:android="http://schemas.android.com/apk/res/android">
  <item android:state_checked="true" android:color="?colorControlActivated" />
  <item android:color="?colorControlNormal" />
</selector>

需要颜色状态列表的小部件:

<RadioButton
  android:id="@+id/radio_button"
  android:text="My Radio" />

最重要的是:

ColorStateList csl = AppCompatResources.getColorStateList(context, R.color.my_color_state);    
RadioButton r = (RadioButton) findViewById(R.id.radio_button);
r.setTextColor(csl);

嗯,这不是最优雅或最短的方法,但这是Android支持库所做的,以使其能够在较旧版本的Android(棒棒糖之前)上运行。

不幸的是,可绘制对象的类似方法不适用于样式属性。


1

我在https://stackoverflow.com/a/59467269/3841352中回答了相同的问题,但我也会在此处发布:

我遇到了同样的问题,截至2019年尚未解决,因此您无法在选择器中将属性作为可绘制对象进行引用。我将分享解决该问题的方法,因为我在此处看不到它。我在错误报告的最后评论中找到了它。

解决方法基本上是创建一个可绘制资源,该资源将是引用属性值的资源。

为了说明您的情况,解决方案将改为:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="?attr/colorPrimary" android:state_enabled="true" android:state_window_focused="false"/>
    <item android:drawable="?attr/colorPrimaryDark" android:state_pressed="true"/>
    <item android:drawable="@android:color/darker_gray" android:state_enabled="false"/>
    <item android:drawable="?attr/colorPrimary"/>
</selector>

您可以将?attr / *替换为可绘制资源:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/colorPrimaryDrawable" android:state_enabled="true" android:state_window_focused="false"/>
    <item android:drawable="@drawable/colorPrimaryDarkDrawable" android:state_pressed="true"/>
    <item android:drawable="@android:color/darker_gray" android:state_enabled="false"/>
    <item android:drawable="@drawable/colorPrimaryDrawable"/>
</selector>

可绘制对象定义为:

drawable / colorPrimaryDrawable

<shape xmlns:android="http://schemas.android.com/apk/res/android"android:shape="rectangle">
    <solid android:color="?attr/colorPrimary" />
</shape>

drawable / colorPrimaryDarkDrawable

<shape xmlns:android="http://schemas.android.com/apk/res/android"android:shape="rectangle">
    <solid android:color="?attr/colorPrimaryDark" />
</shape>

希望能帮助到你!!


0

正如@marmor所说,API 21现在支持此功能。但是对于那些需要支持旧版本Android的人来说,您可以使用此功能。使用v7支持库,您仍然可以在最低SDK级别一直下降到7的应用程序上使用它。

AppCompatImageView在第7版的Android支持库有一个bug免费实现此功能。只需更换你的用法ImageViewAppCompatImageView


2
听起来很有希望!但是问题出在Drawable形状而不是View。我有一个appcompat视图,它引用了一个作为背景的形状(使用背景),该形状使用attr获得主题颜色,并且由于上述错误而落在<21上。是否有我错过的appcompat可绘制对象?
NeilS,2016年

Android 5.0.1 jy华为仍然受到影响。
萨瑟顿
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.