如何引用可绘制的样式属性?

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

这个方法工作得很好,允许我轻松地创建和修改几个主题。

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

 <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,但同样的情况发生了。

我怎么能这么做?为什么它在视野中有效,而在 Drawables 没有效?我在 文件的任何地方都找不到这个限制..。

37699 次浏览

In my experience it is not possible to reference an attribute in an XML drawable.
In order to make your theme you need to:

  • Create one XML drawable per theme.
  • Include the needed color into you drawable directly with the @color tag or #RGB format.

Make an attribute for your drawable in 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>

Add your drawable to your theme.xml.

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

Reference your drawable in your layout using your attribute.

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

Starting with lollipop (API 21) this feature is supported, see https://code.google.com/p/android/issues/detail?id=26251

However, if you're targeting devices without lollipop, don't use it, as it will crash, use the workaround in the accepted answer instead.

As @marmor stated this is now supported on API 21. But for those us who need to support legacy versions of Android, you can use this feature. Using the v7 support library you can still use it on apps with minimum SDK level all the way down to 7.

The AppCompatImageView in the v7 Android Support Library has a bug free implementation of this feature. Simply replace your usages of ImageView with AppCompatImageView.

Although it's not possible to reference style attributes from drawables on pre-Lollipop devices, but it's possible for color state lists. You can use AppCompatResources.getColorStateList(Context context, int resId) method from Android Support Library. The downside is that you will have to set those color state lists programmatically.

Here is a very basic example.

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>

A widget that needs a color state list:

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

And the most important:

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

Well, not the most elegant or shortest way, but this is what Android Support Library does to make it work on older versions (pre-Lollipop) of Android.

Unfortunately, the similar method for drawables doesn't work with style attributes.

I answered the same question in https://stackoverflow.com/a/59467269/3841352 but i will post it here as well:

I encountered the same problem and as of 2019 it hasn't been resolved so you can't have an attribute referenced in a selector as a drawable. I will share the solution I got for the problem as I don't see it posted in here. I found it in the last comment of the bug report.

The workaround is basically create a drawable resource that will be the one referring the attribute value.

To illustrate your case the solution would be instead of:

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

you would replace the ?attr/* for a drawable resource:

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

The drawables would be defined as:

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>

Hope it helps!!