Come fare riferimento agli attributi di stile da un disegnabile?


96

Voglio avere 2 temi selezionabili per la mia applicazione. Per fare ciò, ho definito alcuni attributi, come questo:

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

Quindi, ho creato entrambi i temi, in questo modo:

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

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

Questo metodo funziona alla grande, permettendomi di creare e modificare facilmente diversi temi. Il problema è che sembra che possa essere utilizzato solo in Views e non in Drawables .

Ad esempio, fare riferimento a un valore da una vista all'interno di un layout funziona:

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

Ma fare lo stesso in un Drawable non:

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

Ottengo questo errore durante l'esecuzione dell'applicazione:

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

Se invece di ?item_backgroundutilizzare un colore hardcoded, funziona, ma ciò non mi consente di utilizzare i miei temi. Ho anche provato ?attr:item_background, ma succede lo stesso.

Come potrei farlo? E perché funziona in Views ma non in Drawables? Non riesco a trovare questa limitazione da nessuna parte nella documentazione ...


Questo potrebbe essere un duplicato della seguente domanda: stackoverflow.com/questions/7529574/...
Paul Lammertsma

@Martin M., cosa hai capito con questo?
user123321

Qualche soluzione per questo ancora? Sto colpendo lo stesso identico muro
Guillaume

Un'altra domanda più recente qui: stackoverflow.com/q/12115125/317889 Stesso problema. Risolvilo Google.
HGPB

3
Apparentemente questo problema è stato risolto nell'anteprima di Android L, come specificato qui: code.google.com/p/android/issues/detail?id=26251
Bianca Daniciuc

Risposte:


161

Nella mia esperienza non è possibile fare riferimento a un attributo in un disegnabile XML.
Per creare il tuo tema devi:

  • Crea un disegnabile XML per tema.
  • Includere il colore necessario nel disegnabile direttamente con il @colortag o il formato #RGB.

Crea un attributo per il tuo disegnabile 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>

Aggiungi il tuo disegnabile al tuo theme.xml .

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

Fai riferimento al tuo disegnabile nel tuo layout usando il tuo attributo.

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

20
Sembra che questa soluzione sia ancora necessaria se vuoi mantenere la compatibilità ... Sì da L è corretto e il colore del tema è ben referenziato, ma lo stesso codice gira su diversi dispositivi pre-L e in realtà il riferimento all'attr non funziona su dispositivi pre-L! LoL .. Quindi per me non è corretto se devo mantenere comunque diversi drawables.
Davideas

2
Vorrei poter votare due volte. Ho appena provato a semplificare le mie forme per utilizzare i riferimenti invece di #RGB hardcoded e ho ricevuto lo stesso errore. Sono venuto a SO per una soluzione e ho trovato la stessa risposta che ho votato 2 settimane fa! (facepalm)
Tiksi

2
Questo è super ninja!
Marius Budin

4
ARGH! Questa dovrebbe essere la massima priorità per Google per cui risolvere il backport! La notazione? Attr-reference è così estremamente utile e importante che è quasi impossibile vivere senza se hai più temi all'interno di un'app. In questo momento devo creare tre XML disegnabili per ciascuno dei tipi di pulsanti personalizzati!
Stephan Henningsen

2
Questo è quello che voglio. soluzione molto semplice e piacevole in <21 SDK.
Adnan Abdollah Zaki

20

A partire da lollipop(API 21) questa funzione è supportata, vedere https://code.google.com/p/android/issues/detail?id=26251

Tuttavia, se stai prendendo di mira dispositivi senza lecca-lecca, non usarlo, poiché si bloccherà, usa invece la soluzione alternativa nella risposta accettata.


C'è qualche possibilità di utilizzare un tipo di libreria di supporto per farlo funzionare su API pre L?
Ivan

2
No, la libreria di supporto è un insieme di API che possono essere utilizzate su dispositivi meno recenti, questa modifica del comportamento è nel compilatore e negli strumenti di compilazione, quindi non è correlata alla libreria di supporto.
marmor

3
No, in lollipop non era una funzionalità implementata, ma una evidente correzione di bug. E un grave bug in questo.
Adrian Sicaru

accidenti, pensavo di avere una bella soluzione a tema. ora devo tornare indietro e creare una serie di disegnabili per il selettore di pulsanti.
filthy_wizard

4

Sebbene non sia possibile fare riferimento agli attributi di stile dai drawable sui dispositivi pre-Lollipop , ma è possibile per gli elenchi di stati dei colori. Puoi utilizzare il metodo AppCompatResources.getColorStateList (Context context, int resId) dalla libreria di supporto Android. Lo svantaggio è che dovrai impostare quegli elenchi di stati dei colori a livello di programmazione.

Ecco un esempio molto semplice.

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 che necessita di un elenco di stati dei colori:

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

E il più importante:

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

Beh, non il modo più elegante o più breve, ma questo è ciò che fa la libreria di supporto Android per farlo funzionare su versioni precedenti (pre-Lollipop) di Android.

Sfortunatamente, il metodo simile per i drawable non funziona con gli attributi di stile.


1

Ho risposto alla stessa domanda in https://stackoverflow.com/a/59467269/3841352 ma la posterò anche qui:

Ho riscontrato lo stesso problema e dal 2019 non è stato risolto, quindi non è possibile fare riferimento a un attributo in un selettore come disegnabile. Condividerò la soluzione che ho ottenuto per il problema poiché non la vedo pubblicata qui. L'ho trovato nell'ultimo commento della segnalazione di bug .

La soluzione è fondamentalmente creare una risorsa disegnabile che sarà quella che fa riferimento al valore dell'attributo.

Per illustrare il tuo caso la soluzione sarebbe invece di:

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

sostituiresti? attr / * per una risorsa disegnabile:

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

I drawables sarebbero definiti come:

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>

Spero che sia d'aiuto!!


0

Come affermato da @marmor, questo è ora supportato dall'API 21. Ma per coloro che hanno bisogno di supportare le versioni precedenti di Android, puoi usare questa funzione. Usando la libreria di supporto v7 puoi comunque usarla su app con un livello SDK minimo fino a 7.

La AppCompatImageViewlibreria di supporto Android v7 ha un'implementazione priva di bug di questa funzione. Sostituisci semplicemente i tuoi usi di ImageViewcon AppCompatImageView.


2
Sembra pieno di speranza! Ma il problema è nella forma Drawable non in una vista. Ho una vista appcompat che fa riferimento (come sfondo) a una forma che utilizza attr per ottenere un colore a tema e cade con l'errore precedente su <21. C'è un disegno compatibile con l'app che mi sono perso?
NeilS

Android 5.0.1 jy Huawei ancora interessato.
southerton
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.