How to reference style attributes from a drawable?

2019-01-02 19:41发布

I want to have 2 selectable themes for my application. In order to do that, I defined some attributes, like this:

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

Then, I created both themes, like this:

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

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

This method works great, allowing me to create and modify several themes easily. The problem is that it seems that it can be used only in Views, and not in Drawables.

For example, referencing a value from a View inside a layout works:

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

But doing the same in a Drawable doesn't:

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

I get this error when running the application:

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

If instead of ?item_background I use a hardcoded color, it works, but that doesn't allow me to use my themes. I also tried ?attr:item_background, but the same happens.

How could I do this? And why does it work in Views but not in Drawables? I can't find this limitation anywhere in the documentation...

4条回答
裙下三千臣
2楼-- · 2019-01-02 20:36

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.

查看更多
几人难应
3楼-- · 2019-01-02 20:36

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.

查看更多
十年一品温如言
4楼-- · 2019-01-02 20:40

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" />
查看更多
孤独总比滥情好
5楼-- · 2019-01-02 20:40

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.

查看更多
登录 后发表回答