Jetpack Compose y strings.xml con estilo

Nov 26 2022
Problema Así que tiene una entrada en strings.xml con contenido HTML: ¡Se ha ido todo el estilo! ¿Qué sucedió? Solución Jetpack Compose aún no admite texto con estilo.
Logotipo de Jetpack Compose. Se ve bien, pero no tiene texto con estilo. ¿Porqué es eso?

Problema

Así que tienes una entrada en strings.xml con contenido HTML:

<string name="neatly_styled">Nice <u>underlined</u> and <b>bold</b> text.</string>

@Preview
@Compose
fun PreviewStyledText() {
  Text(text = stringResource(R.string.neatly_styled))
}

Vista previa del texto. ¡Falta estilismo!

¡Todo el estilo se ha ido! ¿Qué sucedió?

Solución

Jetpack Compose aún no admite texto con estilo. Pero es fácil de arreglar.

Siguiendo el problema 139320238 , creamos nuestro propio cargador de recursos de cadena:

@Composable
fun annotatedStringResource(@StringRes id: Int): AnnotatedString {
    val resources = LocalContext.current.resources
    return remember(id) {
        val text = resources.getText(id)
        if (text is Spanned) {
            val spanStyles = mutableListOf<AnnotatedString.Range<SpanStyle>>()
            spanStyles.addAll(text.getSpans(0, text.length, UnderlineSpan::class.java).map {
                AnnotatedString.Range(
                    SpanStyle(textDecoration = TextDecoration.Underline),
                    text.getSpanStart(it),
                    text.getSpanEnd(it)
                )
            })
            spanStyles.addAll(text.getSpans(0, text.length, StyleSpan::class.java).map {
                AnnotatedString.Range(
                    SpanStyle(fontWeight = FontWeight.Bold),
                    text.getSpanStart(it),
                    text.getSpanEnd(it)
                )
            })
            AnnotatedString(text.toString(), spanStyles = spanStyles)
        } else {
            AnnotatedString(text.toString())
        }
    }
}

@Preview
@Composable
fun PreviewStyledTextFix() {
    Text(text = annotatedStringResource(R.string.styled_text))
}

Vista previa del texto. Todavía no hay estilo, porque no es compatible con la versión preliminar

Eh qué… ¿Aún NO ESTÁS ESTILO? La cuestión es que el modo de vista previa tampoco admite texto con estilo.

Pero, un dispositivo real sí:

Vista previa de texto en un dispositivo. ¡El estilo está ahí!

Y como nuestros usuarios solo usan dispositivos reales, todos estamos bien.

Bajo el capó

El componente Jetpack Compose Text admite texto con estilo. El soporte se llama AnnotatedString. Entonces, si podemos proporcionar Texto con AnnotatedStrings, todo está bien.

Desde el lado de Android, proporciona dos formas de obtener una cadena localizada:

  • String getString(id), que devuelve un String. Eso no puede contener ningún estilo y las etiquetas HTML simplemente se eliminan y se ignoran.
  • CharSequence getText(id), que devuelve una CharSequence. El problema es que esta secuencia también contiene los intervalos de estilo definidos en HTML.