¿Cómo hacer referencia a los atributos de estilo de un dibujable?

96

Quiero tener 2 temas seleccionables para mi aplicación. Para hacer eso, definí algunos atributos, como este:

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

Luego, creé ambos temas, así:

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

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

Este método funciona muy bien, permitiéndome crear y modificar varios temas fácilmente. El problema es que parece que solo se puede usar en Views y no en Drawables .

Por ejemplo, hacer referencia a un valor de una Vista dentro de un diseño funciona:

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

Pero hacer lo mismo en un Drawable no:

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

Recibo este error al ejecutar la aplicación:

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

Si en lugar de ?item_backgroundusar un color codificado, funciona, pero eso no me permite usar mis temas. También lo intenté ?attr:item_background, pero pasa lo mismo.

¿Cómo podría hacer esto? ¿Y por qué funciona en Views pero no en Drawables? No puedo encontrar esta limitación en ninguna parte de la documentación ...

MM.
fuente
Esto podría ser un duplicado de la siguiente pregunta: stackoverflow.com/questions/7529574/…
Paul Lammertsma
@Martin M., ¿qué descubriste con esto?
user123321
¿Alguna solución para eso todavía? Estoy golpeando exactamente la misma pared
Guillaume
Otra pregunta más reciente aquí: stackoverflow.com/q/12115125/317889 Mismo problema. Ordénelo Google.
HGPB
3
Aparentemente, este problema se resolvió en la vista previa de Android L, como se especifica aquí: code.google.com/p/android/issues/detail?id=26251
Bianca Daniciuc

Respuestas:

161

En mi experiencia, no es posible hacer referencia a un atributo en un elemento de diseño XML.
Para crear tu tema necesitas:

  • Cree un elemento de diseño XML por tema.
  • Incluya el color necesario en su dibujo directamente con la @coloretiqueta o el formato #RGB.

Cree un atributo para su elemento de dibujo en 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>

Agregue su elemento de dibujo a su theme.xml .

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

Haga referencia a su dibujable en su diseño usando su atributo.

<TextView android:background="?my_drawable" />
LG
fuente
20
Parece que esta solución sigue siendo necesaria si desea mantener la compatibilidad ... Sí, desde L, está fijo y el color del tema está bien referenciado, pero el mismo código se ejecuta en diferentes dispositivos pre-L y, en realidad, la referencia de atributo no funciona en dispositivos pre-L! LoL .. Entonces para mí no es fijo si tengo que mantener diferentes elementos de diseño de todos modos.
Davideas
2
Ojalá pudiera votar a favor de esto dos veces. Intenté simplificar mis formas para usar referencias en lugar de #RGB codificado y obtuve el mismo error. ¡Vine a SO en busca de una solución y encontré esta misma respuesta que voté a favor hace 2 semanas! (facepalm)
Tiksi
2
¡Esto es super ninja!
MariusBudin
4
ARGH! ¡Esto debería ser la máxima prioridad para que Google haga una corrección de backport! La notación? Attr-reference es tan extremadamente útil e importante que es casi imposible vivir sin ella si tienes varios temas dentro de una aplicación. ¡Ahora mismo tengo que hacer tres XML dibujables para cada uno de los tipos de botones personalizados!
Stephan Henningsen
2
Eso es lo que quiero. solución muy simple y agradable en <21 SDK.
Adnan Abdollah Zaki
20

A partir de lollipop(API 21), esta función es compatible, consulte https://code.google.com/p/android/issues/detail?id=26251

Sin embargo, si está apuntando a dispositivos sin piruleta, no lo use, ya que se bloqueará, use la solución alternativa en la respuesta aceptada.

marmor
fuente
¿Existe alguna posibilidad de utilizar algún tipo de biblioteca de soporte para que funcione en una API anterior a L?
Ivan
2
No, la biblioteca de soporte es un conjunto de API que se puede usar en dispositivos más antiguos, este cambio de comportamiento está en el compilador y las herramientas de compilación, por lo que no está relacionado con la biblioteca de soporte.
marmor
3
No, en Lollipop no era una función implementada, sino una corrección de errores evidente. Y un error grave en eso.
Adrian Sicaru
Maldita sea, pensé que tenía una buena solución temática. ahora tengo que volver atrás y crear un montón de elementos de diseño de selector de botones.
filthy_wizard
4

Aunque no es posible hacer referencia a atributos de estilo de elementos de diseño en dispositivos anteriores a Lollipop , es posible para listas de estado de color. Puede usar el método AppCompatResources.getColorStateList (Context context, int resId) de la biblioteca de compatibilidad de Android. La desventaja es que tendrá que configurar esas listas de estado de color mediante programación.

He aquí un ejemplo muy básico.

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 que necesita una lista de estado de color:

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

Y lo mas importante:

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

Bueno, no es la forma más elegante o más corta, pero esto es lo que hace la Biblioteca de soporte de Android para que funcione en versiones anteriores (anteriores a Lollipop) de Android.

Desafortunadamente, el método similar para los elementos de diseño no funciona con los atributos de estilo.

rubo
fuente
1

Respondí la misma pregunta en https://stackoverflow.com/a/59467269/3841352 pero también la publicaré aquí:

Encontré el mismo problema y, a partir de 2019, no se ha resuelto, por lo que no se puede hacer referencia a un atributo en un selector como dibujable. Compartiré la solución que obtuve para el problema, ya que no la veo publicada aquí. Lo encontré en el último comentario del informe de errores .

La solución es básicamente crear un recurso dibujable que será el que se refiera al valor del atributo.

Para ilustrar su caso, la solución sería en lugar 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>

reemplazaría el? attr / * por un recurso dibujable:

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

Los dibujables se definirían como:

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

¡¡Espero eso ayude!!

Pol
fuente
0

Como dijo @marmor, esto ahora es compatible con API 21. Pero para aquellos que necesitamos admitir versiones heredadas de Android, pueden usar esta función. Con la biblioteca de soporte v7, aún puede usarla en aplicaciones con un nivel mínimo de SDK hasta 7.

La AppCompatImageViewbiblioteca de soporte de Android v7 tiene una implementación sin errores de esta función. Simplemente reemplace sus usos de ImageViewcon AppCompatImageView.

Benjamín
fuente
2
¡Suena esperanzador! Pero el problema está en la forma dibujable, no en una vista. Tengo una vista de appcompat que hace referencia (como fondo) a una forma que usa attr para obtener un color temático y se cae con el error anterior en <21. ¿Hay algún elemento de dibujo de appcompat que me haya perdido?
NeilS
Android 5.0.1 jy Huawei sigue afectado.
southerton