Jetpack Compose и стилизованный файл strings.xml

Nov 26 2022
Проблема Итак, у вас есть запись в файле strings.xml с содержимым HTML: Все стили исчезли! Что произошло? Решение Jetpack Compose пока не поддерживает стилизованный текст.
Логотип Jetpack Compose. Выглядит красиво, но не имеет стилизованного текста. Почему это?

Проблема

Итак, у вас есть запись в strings.xml с содержимым 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))
}

Предварительный просмотр текста. Стайлинг отсутствует!

Весь стайлинг пропал! Что произошло?

Решение

Jetpack Compose пока не поддерживает стилизованный текст. Но это просто исправить.

После проблемы 139320238 мы создаем собственный загрузчик строковых ресурсов:

@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))
}

Предварительный просмотр текста. По-прежнему нет стиля, потому что он не поддерживается в предварительной версии.

Э, что… Все еще НЕ СТИЛИРОВАНО? Дело в том, что режим предварительного просмотра также не поддерживает стилизованный текст.

Но реальное устройство делает:

Предварительный просмотр текста на устройстве. Стайлинг есть!

И поскольку наши пользователи используют только настоящие устройства, у нас все в порядке.

Под капотом

Компонент Jetpack Compose Text поддерживает стилизованный текст. Поддержка называется AnnotatedString. Так что, если мы можем просто предоставить Text с AnnotatedStrings, у нас все в порядке.

Со стороны Android он предоставляет два способа получить локализованную строку:

  • Строка getString(id), которая возвращает строку. Это не может содержать никаких стилей, а теги HTML просто удаляются и игнорируются.
  • CharSequence getText(id), который возвращает CharSequence. Загвоздка в том, что эта последовательность также содержит диапазоны стилей, определенные в HTML.