How to reference style attributes from a drawable?
Asked Answered
H

5

102

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

Hypochromia answered 7/11, 2011 at 19:34 Comment(7)
This might be a duplicate of the following question: #7530074Incretion
@Martin M., what did you figure out with this?Whim
Any solution for that yet? I'm hitting the exact same wallTalmudist
Another more recent question here: https://mcmap.net/q/212137/-creating-custom-style-crashes-app/317889 Same problem. Sort it out Google.Ayo
Apparently this issue was solved in Android L preview, as specified here: code.google.com/p/android/issues/detail?id=26251Glidebomb
Wow! Better late than never I guess :)Hypochromia
Thanks to this question... It's nice to know that it is fixed but our code runs on different pre-L devices. So we have to maintain different drawables anyway. Can somebody confirm?Dispensation
A
179

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" />
Antechamber answered 20/11, 2012 at 10:55 Comment(9)
It seems that, this solution is still necessary if you want keep compatibility... Yes from L it is fixed and Theme color is well referenced, but same code runs on different pre-L devices and actually the attr reference doesn't work on pre-L devices! LoL.. So for me it is not fixed if I have to maintain different drawables anyway.Dispensation
I wish I could upvote this twice. Just tried to streamline my shapes to use references instead of hardcoded #RGB and got the same error. Came to SO for a solution and found this same answer that I upvoted 2 weeks ago! (facepalm)Cinquecento
before posting a new question I wanna ask here, I'm having the "type=0x2" on this <ProgressBar android:id="@+id/progressbar" android:progressDrawable="@drawable/progress_single_input_form" android:layout_width="match_parent" android:layout_gravity="bottom"/> What I'm missing? (that part of a biger xml, and when I remove it, everything else works), this only happens on andorid 4.2.2Trooper
Difficult to answer with so few informations, I recommend you to post a question with a detailed description of the problem.Antechamber
ARGH! This should be top priority for Google to make a backport-fix for! The ?attr-reference notation is so extremely useful and important that it's so close to impossible to live without if you have multiple themes within an app. Right now I have to make three drawable XMLs for each of custom button types!Cap
Hi. It is possible reference custom attr from drawable but on API 21 and above. Paste an example.Anorak
Thats i want . very simple and nice solution in < 21 SDK .Guest
That's easy, but, for a new theme, have to add new files for all the UI elements, that's long taskEssequibo
But there is another error when I implemented it Caused by: android.content.res.Resources$NotFoundException: Resource is not a Drawable (color or path): TypedValue{t=0x2/d=0x7f040134 a=-1}Drambuie
P
20

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.

Proclitic answered 20/5, 2015 at 13:47 Comment(4)
Is there any chance to use kind of support library to make it work on a pre L APIs ?Margrettmarguerie
No, the support library is a set of APIs that can be used on older devices, this behavior change is in the compiler and build tools, so it's not related to the support library.Proclitic
No, in lollipop it was not an implemented feature, but a glaring bug fix. And a serious bug at that.Inharmonious
damn, i thought i had a nice theming solution. now I have to go back and create a bunch of button selector drawables.Veterinary
P
5

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.

Pendulous answered 10/12, 2016 at 23:59 Comment(0)
H
5

I answered the same question in https://mcmap.net/q/212138/-android-selector-drawable-doesnt-work-with-attributes 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!!

Haig answered 24/12, 2019 at 10:23 Comment(1)
The problem is that you can't reference a theme attribute in a shape drawable. You're still doing that.Bithia
H
0

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.

Herisau answered 11/5, 2016 at 3:51 Comment(2)
Sounds hopeful! But the problem is in the Drawable shape not a View. I have an appcompat view referencing (as background) a shape which uses attr to get a themed colour and it falls over with the above error on <21. Is there an appcompat drawable I've missed?Katharina
Android 5.0.1 jy Huawei still affected.Charo

© 2022 - 2024 — McMap. All rights reserved.