【问题标题】:How to reference style attributes from a drawable?如何从可绘制对象中引用样式属性?
【发布时间】:2011-12-23 21:37:27
【问题描述】:

我想为我的应用程序提供 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里

例如,从布局内的视图中引用值是可行的:

 <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 中无效?我在the documentation 的任何地方都找不到这个限制...

【问题讨论】:

标签: android android-drawable android-theme android-attributes


【解决方案1】:

根据我的经验,无法在 XML 可绘制对象中引用属性。
为了制作您的主题,您需要:

  • 为每个主题创建一个 XML 可绘制对象。
  • 使用@color 标签或#RGB 格式将所需的颜色直接包含到您的drawable 中。

attrs.xml中为你的drawable创建一个属性。

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

将你的drawable添加到你的theme.xml

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

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

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

【讨论】:

  • 看来,如果你想保持兼容性,这个解决方案仍然是必要的......从 L 是的,它是固定的,主题颜色被很好地引用,但相同的代码在不同的 pre-L 设备上运行,实际上attr 参考在 L 之前的设备上不起作用!大声笑..所以对我来说,如果我必须维护不同的drawables,这不是固定的。
  • 我希望我能投票两次。只是试图简化我的形状以使用引用而不是硬编码的#RGB 并得到相同的错误。来到 SO 寻求解决方案,并找到了我两周前赞成的相同答案! (捂脸)
  • 这是超级忍者!
  • 啊!这应该是谷歌进行反向移植修复的首要任务! ?attr-reference 符号非常有用和重要,如果您在应用程序中有多个主题,那么几乎不可能没有它。现在我必须为每种自定义按钮类型制作三个可绘制的 XML!
  • 这就是我想要的。
【解决方案2】:

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

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

【讨论】:

  • 是否有机会使用某种支持库使其在预 L API 上工作?
  • 不,支持库是一组可以在旧设备上使用的API,这种行为变化是在编译器和构建工具中,所以它与支持库无关。
  • 不,在棒棒糖中,它不是一个实现的功能,而是一个明显的错误修复。还有一个严重的错误。
  • 该死的,我以为我有一个不错的主题解决方案。现在我必须回去创建一堆按钮选择器可绘制对象。
【解决方案3】:

虽然在 棒棒糖之前 设备上无法从可绘制对象中引用样式属性,但对于颜色状态列表是可能的。您可以使用 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 的旧版本(Lollipop 之前)上工作所做的工作。

很遗憾,drawable 的 similar method 不适用于样式属性。

【讨论】:

    【解决方案4】:

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

    我遇到了同样的问题,截至 2019 年尚未解决,因此您不能将选择器中引用的属性作为可绘制对象。我将分享我为这个问题得到的解决方案,因为我没有看到它在这里发布。我在bug report的最后一条评论中找到了。

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

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

    <?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 将被定义为:

    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>
    

    希望对你有帮助!!

    【讨论】:

      【解决方案5】:

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

      v7 Android 支持库中的AppCompatImageView 具有此功能的无错误实现。只需将 ImageView 的用法替换为 AppCompatImageView

      【讨论】:

      • 听起来很有希望!但问题在于可绘制形状而不是视图。我有一个 appcompat 视图引用(作为背景)一个形状,它使用 attr 来获得主题颜色,并且它在
      • Android 5.0.1 jy 华为仍受影响。