Comment référencer des attributs de style à partir d'un dessinable?

96

Je souhaite avoir 2 thèmes sélectionnables pour mon application. Pour ce faire, j'ai défini quelques attributs, comme ceci:

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

Ensuite, j'ai créé les deux thèmes, comme ceci:

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

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

Cette méthode fonctionne très bien, me permettant de créer et de modifier facilement plusieurs thèmes. Le problème est qu'il semble qu'il ne puisse être utilisé que dans Views, et non dans Drawables .

Par exemple, le référencement d'une valeur à partir d'une vue à l'intérieur d'une mise en page fonctionne:

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

Mais faire la même chose dans un Drawable ne permet pas:

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

J'obtiens cette erreur lors de l'exécution de l'application:

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

Si au lieu d' ?item_backgroundutiliser une couleur codée en dur, cela fonctionne, mais cela ne me permet pas d'utiliser mes thèmes. J'ai aussi essayé ?attr:item_background, mais la même chose se produit.

Comment pourrais-je faire ça? Et pourquoi cela fonctionne-t-il dans Views mais pas dans Drawables? Je ne trouve cette limitation nulle part dans la documentation ...

MM.
la source
Cela pourrait être un double de la question suivante: stackoverflow.com/questions/7529574/…
Paul Lammertsma
@Martin M., qu'avez-vous compris avec ça?
user123321
Une solution pour ça encore? Je frappe exactement le même mur
Guillaume
Une autre question plus récente ici: stackoverflow.com/q/12115125/317889 Même problème. Faites le tri sur Google.
HGPB
3
Apparemment, ce problème a été résolu dans l'aperçu d'Android L, comme spécifié ici: code.google.com/p/android/issues/detail?id=26251
Bianca Daniciuc

Réponses:

161

D'après mon expérience, il n'est pas possible de référencer un attribut dans un dessin XML.
Pour créer votre thème, vous devez:

  • Créez un dessin XML par thème.
  • Incluez la couleur nécessaire dans votre dessin directement avec la @colorbalise ou le format #RGB.

Créez un attribut pour votre dessinable dans 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>

Ajoutez votre dessinable à votre theme.xml .

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

Faites référence à votre dessinable dans votre mise en page en utilisant votre attribut.

<TextView android:background="?my_drawable" />
LG
la source
20
Il semble que cette solution soit toujours nécessaire si vous voulez conserver la compatibilité ... Oui à partir de L, elle est fixe et la couleur du thème est bien référencée, mais le même code fonctionne sur différents appareils pré-L et en fait la référence attr ne fonctionne pas sur appareils pré-L! LoL .. Donc pour moi ce n'est pas fixe si je dois quand même maintenir différents drawables.
Davideas
2
J'aurais aimé pouvoir voter deux fois. J'ai juste essayé de rationaliser mes formes pour utiliser des références au lieu de #RGB codé en dur et j'ai eu la même erreur. Je suis venu chez SO pour une solution et j'ai trouvé la même réponse que j'ai votée il y a 2 semaines! (facepalm)
Tiksi
2
C'est super ninja!
MariusBudin
4
ARGH! Cela devrait être la priorité absolue pour Google de faire un correctif de backport! La notation? Attr-reference est si extrêmement utile et importante qu'il est presque impossible de s'en passer si vous avez plusieurs thèmes dans une application. Pour le moment, je dois créer trois fichiers XML dessinables pour chacun des types de boutons personnalisés!
Stephan Henningsen
2
C'est ce que je veux. solution très simple et agréable dans <21 SDK.
Adnan Abdollah Zaki
20

À partir de lollipop(API 21), cette fonctionnalité est prise en charge, voir https://code.google.com/p/android/issues/detail?id=26251

Cependant, si vous ciblez des appareils sans sucette, ne l'utilisez pas, car cela plantera, utilisez plutôt la solution de contournement dans la réponse acceptée.

marmor
la source
Y a-t-il une chance d'utiliser un type de bibliothèque de support pour le faire fonctionner sur une API pré-L?
Ivan
2
Non, la bibliothèque de support est un ensemble d'API qui peuvent être utilisées sur des appareils plus anciens, ce changement de comportement est dans les outils de compilation et de génération, donc il n'est pas lié à la bibliothèque de support.
marmor
3
Non, dans Lollipop, ce n'était pas une fonctionnalité implémentée, mais une correction de bogue flagrante. Et un bug sérieux à cela.
Adrian Sicaru
putain, je pensais avoir une belle solution de thème. maintenant je dois revenir en arrière et créer un tas de dessinables de sélecteur de bouton.
filthy_wizard
4

Bien qu'il ne soit pas possible de référencer des attributs de style à partir de dessinables sur des appareils pré-Lollipop , mais c'est possible pour les listes d'état de couleur. Vous pouvez utiliser la méthode AppCompatResources.getColorStateList (Context context, int resId) de la bibliothèque de support Android. L'inconvénient est que vous devrez définir ces listes d'états de couleur par programmation.

Voici un exemple très basique.

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>

Un widget qui a besoin d'une liste d'état des couleurs:

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

Et le plus important:

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

Eh bien, ce n'est pas le moyen le plus élégant ou le plus court, mais c'est ce que fait la bibliothèque de support Android pour la faire fonctionner sur les anciennes versions (pré-Lollipop) d'Android.

Malheureusement, la méthode similaire pour les drawables ne fonctionne pas avec les attributs de style.

rubo
la source
1

J'ai répondu à la même question sur https://stackoverflow.com/a/59467269/3841352 mais je la posterai également ici:

J'ai rencontré le même problème et à partir de 2019, il n'a pas été résolu, vous ne pouvez donc pas avoir un attribut référencé dans un sélecteur comme dessinable. Je vais partager la solution que j'ai obtenue pour le problème car je ne la vois pas publiée ici. Je l'ai trouvé dans le dernier commentaire du rapport de bogue .

La solution de contournement consiste essentiellement à créer une ressource dessinable qui sera celle faisant référence à la valeur d'attribut.

Pour illustrer votre cas, la solution serait au lieu de:

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

vous remplaceriez le? attr / * pour une ressource dessinable:

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

Les drawables seraient définis comme:

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>

J'espère que ça aide!!

Pol
la source
0

Comme @marmor l'a indiqué, cela est désormais pris en charge sur l'API 21. Mais pour ceux qui ont besoin de prendre en charge les anciennes versions d'Android, vous pouvez utiliser cette fonctionnalité. En utilisant la bibliothèque de support v7, vous pouvez toujours l'utiliser sur des applications avec un niveau minimum de SDK jusqu'à 7.

La AppCompatImageViewbibliothèque de support Android v7 a une implémentation sans bogue de cette fonctionnalité. Remplacez simplement vos utilisations de ImageViewpar AppCompatImageView.

Benjamin
la source
2
Sonne d'espoir! Mais le problème réside dans la forme Drawable et non dans une vue. J'ai une vue appcompat référençant (en arrière-plan) une forme qui utilise attr pour obtenir une couleur à thème et elle tombe avec l'erreur ci-dessus sur <21. Y a-t-il un dessinable appcompat que j'ai manqué?
NeilS
Android 5.0.1 jy Huawei toujours affecté.
Southerton