How to reference style attributes from a drawable?

36,886

Solution 1

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" />

Solution 2

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.

Solution 3

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.

Solution 4

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

Share:
36,886

Related videos on Youtube

MM.
Author by

MM.

Updated on December 25, 2020

Comments

  • MM.
    MM. over 3 years

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

    • Bahadır Yıldırım
      Bahadır Yıldırım over 12 years
      This might be a duplicate of the following question: stackoverflow.com/questions/7529574/…
    • user123321
      user123321 over 12 years
      @Martin M., what did you figure out with this?
    • Guillaume
      Guillaume over 12 years
      Any solution for that yet? I'm hitting the exact same wall
    • HGPB
      HGPB almost 12 years
      Another more recent question here: stackoverflow.com/q/12115125/317889 Same problem. Sort it out Google.
    • Bianca Daniciuc
      Bianca Daniciuc almost 10 years
      Apparently this issue was solved in Android L preview, as specified here: code.google.com/p/android/issues/detail?id=26251
    • MM.
      MM. almost 10 years
      Wow! Better late than never I guess :)
    • Davideas
      Davideas about 9 years
      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?
  • Davideas
    Davideas about 9 years
    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.
  • Tiksi
    Tiksi almost 9 years
    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)
  • Ivan
    Ivan over 8 years
    Is there any chance to use kind of support library to make it work on a pre L APIs ?
  • marmor
    marmor over 8 years
    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.
  • Adrian Sicaru
    Adrian Sicaru over 8 years
    No, in lollipop it was not an implemented feature, but a glaring bug fix. And a serious bug at that.
  • Umair
    Umair over 8 years
    Thank you mate. You're a rockstar!!
  • MariusBudin
    MariusBudin over 8 years
    This is super ninja!
  • Camilo Casadiego
    Camilo Casadiego over 8 years
    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_fo‌​rm" 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.2
  • L. G.
    L. G. over 8 years
    Difficult to answer with so few informations, I recommend you to post a question with a detailed description of the problem.
  • filthy_wizard
    filthy_wizard over 8 years
    damn, i thought i had a nice theming solution. now I have to go back and create a bunch of button selector drawables.
  • NeilS
    NeilS almost 8 years
    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?
  • Stephan Henningsen
    Stephan Henningsen over 7 years
    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!
  • Santiago SR
    Santiago SR over 7 years
    Hi. It is possible reference custom attr from drawable but on API 21 and above. Paste an example.
  • Adnan Abdollah Zaki
    Adnan Abdollah Zaki over 7 years
    Thats i want . very simple and nice solution in < 21 SDK .
  • Shamsul Arefin
    Shamsul Arefin about 6 years
    That's easy, but, for a new theme, have to add new files for all the UI elements, that's long task
  • maXp
    maXp about 6 years
    Google WTF??? Why why why?... is any simple task in android always very difficult?! WTF?!
  • HendraWD
    HendraWD about 6 years
    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}
  • southerton
    southerton over 4 years
    Android 5.0.1 jy Huawei still affected.