Cómo recuperar atributos de estilo mediante programación de styles.xml

81

Actualmente estoy usando WebView o TextView para mostrar algunos datos dinámicos provenientes de un servicio web en una de mis aplicaciones. Si los datos contienen texto puro, usa TextView y aplica un estilo de styles.xml. Si los datos contienen HTML (principalmente texto e imágenes), utiliza WebView.

Sin embargo, este WebView no tiene estilo. Por lo tanto, se ve muy diferente del TextView habitual. Leí que es posible diseñar el texto en un WebView simplemente insertando algo de HTML directamente en los datos. Esto suena bastante fácil, pero me gustaría usar los datos de mi Styles.xml como los valores requeridos en este HTML, por lo que no necesitaré cambiar los colores, etcétera, en dos ubicaciones si cambio mis estilos.

Entonces, ¿cómo podría hacer esto? He realizado una búsqueda exhaustiva, pero no he encontrado forma de recuperar los diferentes atributos de estilo de su styles.xml. ¿Me estoy perdiendo algo aquí o realmente no es posible recuperar estos valores?

El estilo del que estoy tratando de obtener los datos es el siguiente:

<style name="font4">
    <item name="android:layout_width">fill_parent</item>
    <item name="android:layout_height">wrap_content</item>
    <item name="android:textSize">14sp</item>
    <item name="android:textColor">#E3691B</item>
    <item name="android:paddingLeft">5dp</item>
    <item name="android:paddingRight">10dp</item>
    <item name="android:layout_marginTop">10dp</item>
    <item name="android:textStyle">bold</item>
</style>

Estoy interesado principalmente en textSize y textColor.

Sander van't Veer
fuente
¿Por qué no analiza el XML (por ejemplo, el analizador SAX de Android)?
m0skit0

Respuestas:

174

Es posible recuperar estilos personalizados mediante styles.xmlprogramación.

Defina algún estilo arbitrario en styles.xml:

<style name="MyCustomStyle">
    <item name="android:textColor">#efefef</item>
    <item name="android:background">#ffffff</item>
    <item name="android:text">This is my text</item>
</style>

Ahora, recupera los estilos como este

// The attributes you want retrieved
int[] attrs = {android.R.attr.textColor, android.R.attr.background, android.R.attr.text};

// Parse MyCustomStyle, using Context.obtainStyledAttributes()
TypedArray ta = obtainStyledAttributes(R.style.MyCustomStyle, attrs);

// Fetch the text from your style like this.     
String text = ta.getString(2);

// Fetching the colors defined in your style
int textColor = ta.getColor(0, Color.BLACK);
int backgroundColor = ta.getColor(1, Color.BLACK);

// Do some logging to see if we have retrieved correct values
Log.i("Retrieved text:", text);
Log.i("Retrieved textColor as hex:", Integer.toHexString(textColor));
Log.i("Retrieved background as hex:", Integer.toHexString(backgroundColor));

// OH, and don't forget to recycle the TypedArray
ta.recycle()
Viejo
fuente
Compañero de trabajo impresionante. Después de que te dijeran (y lo leyeran mucho) que no se podía hacer, demostraste perfectamente que sí. Simplemente lo probé y pude obtener el color de fuente de mi estilo. Todavía no puedo obtener el tamaño del texto, pero lo resolveré. Recibirá 100 puntos tan pronto como pueda enviar la recompensa (dentro de 18 horas). ¡Muchas gracias!
Sander van't Veer
4
¿También es posible cambiar los valores definidos en el estilo en tiempo de ejecución?
ekjyot
¿Qué pasa si quiero cambiar el color de los atributos recuperados? ¿Te gusta cambiar el fondo o el color del texto de forma dinámica dentro de este código?
MSIslam
2
Tengo un problema con esta línea: 'TypedArray ta = getStyledAttributes (R.style.MyCustomStyle, attrs);', Android Studio me dice que espera un recurso de tipo styleable y no veo ninguna forma de suprimir las anotaciones en el biblioteca de apoyo. Supongo que getStyledAttributes tiene una anotación @ResStyleable que me impide pasar un estilo como int. ¿Algunas ideas?
Ben Pearson
1
@BenPearson: tuve el mismo problema que cuando intentabas obtener el atributo textSize. La respuesta de PrivatMamtora funciona y debería ser la respuesta aceptada.
thecoolmacdude
52

La respuesta que ha dado @Ole parece romperse cuando se usan ciertos atributos, como shadowColor, shadowDx, shadowDy, shadowRadius (estos son solo algunos que encontré, podría haber más)

No tengo idea de por qué ocurre este problema, por lo que estoy preguntando aquí , pero el estilo de codificación de @AntoineMarques parece resolver el problema.

Para que esto funcione con cualquier atributo, sería algo como esto


Primero, defina un estilo que contenga los ID de recursos como tal

attrs.xml

<resources>     
    <declare-styleable name="MyStyle" >
        <attr name="android:textColor" />
        <attr name="android:background" />
        <attr name="android:text" />
    </declare-styleable>
</resources>

Luego, en el código, haría esto para obtener el texto.

TypedArray ta = obtainStyledAttributes(R.style.MyCustomStyle, R.styleable.MyStyle);  
String text = ta.getString(R.styleable.MyStyle_android_text);

La ventaja de utilizar este método es que está recuperando el valor por nombre y no por índice.

PrivatMamtora
fuente
3
Esta debería ser la respuesta aceptada, ya que la respuesta de Ole generó una advertencia de "recurso esperado de tipo estilo" para mí.
thecoolmacdude
1
Funciona para mí, pero con Android Studio necesito agregarle un prefijo @SuppressWarnings("ResourceType").
2015
2
Esta respuesta debería ser la aceptada. Es un trabajo para todos mis atributos, y el más votado me hizo perder mucho tiempo.
Jerry
1
Agradezca al poderoso delfín por su solución. La respuesta aceptada me creó problemas para atributos como el fondo (cuando se usaba dibujable) y el tamaño del texto. Esta solución no solo funciona para todos los atributos que necesitaba, sino que también proporciona un código más limpio.
Marius P.
2
¿Qué se supone que es R.style.MyCustomStyle?
janosch
1

Las respuestas de Ole y PrivatMamtora no funcionaron bien para mí, pero esto sí.

Digamos que quería leer este estilo mediante programación:

<style name="Footnote">
    <item name="android:fontFamily">@font/some_font</item>
    <item name="android:textSize">14sp</item>
    <item name="android:textColor">@color/black</item>
</style>

Podría hacerlo así:

fun getTextColorSizeAndFontFromStyle(
    context: Context, 
    textAppearanceResource: Int // Can be any style in styles.xml like R.style.Footnote
) {
    val typedArray = context.obtainStyledAttributes(
        textAppearanceResource,
        R.styleable.TextAppearance // These are added to your project automatically.
    )
    val textColor = typedArray.getColorStateList(
        R.styleable.TextAppearance_android_textColor
    )
    val textSize = typedArray.getDimensionPixelSize(
        R.styleable.TextAppearance_android_textSize
    )

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        val typeface = typedArray.getFont(R.styleable.TextAppearance_android_fontFamily)

        // Do something with the typeface...

    } else {
        val fontFamily = typedArray.getString(R.styleable.TextAppearance_fontFamily)
            ?: when (typedArray.getInt(R.styleable.TextAppearance_android_typeface, 0)) {
                1 -> "sans"
                2 -> "serif"
                3 -> "monospace"
                else -> null
            }

        // Do something with the fontFamily...
    }
    typedArray.recycle()
}

Me inspiré en la clase TextAppearanceSpan de Android, puedes encontrarla aquí: https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/text/style/TextAppearanceSpan.java

Janosch
fuente
-3

Si la solución aceptada no funciona, intente cambiar el nombre de attr.xml a attrs.xml (funcionó para mí)

Юра Перменко
fuente