简体   繁体   English

Android Jetpack Compose:如何显示字符串资源中的样式文本

[英]Android Jetpack Compose: How to show styled Text from string resources

I have a string in my strings.xml which is localized for different languages.我的strings.xml有一个字符串,它针对不同的语言进行了本地化。 The strings are styled with Html tags for each localization.字符串的样式为每个本地化的 Html 标签。

Using Android TextView , I was able to show the styled text just fine by reading the string resources.使用 Android TextView ,我能够通过读取字符串资源来很好地显示样式文本。

Considering that Jetpack Compose currently (1.0.0-rc02) does not support Html tags, I tried using TextView inside an AndroidView composable following Official Docs: https://developer.android.com/jetpack/compose/interop/interop-apis#views-in-compose考虑到 Jetpack Compose 当前(1.0.0-rc02)不支持 Html 标签,我尝试在AndroidView可组合中使用TextView内的官方文档: https : //developer.android.com/jetpack/compose/interop/interop-apis#组合视图

Example of what I tried:我尝试过的示例:

@Composable
fun StyledText(text: String, modifier: Modifier = Modifier) {
    AndroidView(
            modifier = modifier,
            factory = { context -> TextView(context) },
            update = {
                it.text = HtmlCompat.fromHtml(html, HtmlCompat.FROM_HTML_MODE_COMPACT)
            }
    )
}

The text in strings.xml file: strings.xml文件中的文本:

<string name="styled_text">Sample text with <b>bold styling</b> to test</string>

However, using stringResource(id = R.string.styled_text) provides the text without the Html tags.但是,使用stringResource(id = R.string.styled_text)提供没有Html 标签的文本。

Is there a way to show text from string resources with Html styles in Jetpack Compose?有没有办法在 Jetpack Compose 中使用 Html 样式显示字符串资源中的文本?


The following two questions are similar, but they do not read the string from resources:以下两个问题类似,但从资源中读取字符串:

Jetpack compose display html in text Jetpack compose 在文本中显示 html

Android Compose: How to use HTML tags in a Text view Android Compose:如何在文本视图中使用 HTML 标签

stringResource under the hood uses resources.getString , which discards any styled information. stringResource下的stringResource使用resources.getString ,它丢弃任何样式信息。 You need to create something like textResource to get the raw value:您需要创建类似textResource东西来获取原始值:

@Composable
@ReadOnlyComposable
fun textResource(@StringRes id: Int): CharSequence =
    LocalContext.current.resources.getText(id)

And use it like this:并像这样使用它:

StyledText(textResource(id = R.string.foo))

@Composable
fun StyledText(text: CharSequence, modifier: Modifier = Modifier) {
    AndroidView(
        modifier = modifier,
        factory = { context -> TextView(context) },
        update = {
            it.text = text
        }
    )
}

Currently, you can use AnnotatedString class in order to show styled test within a Text component.目前,您可以使用AnnotatedString类在Text组件中显示样式测试。 I will show you an example:我将向您展示一个示例:

Text(
    text = buildAnnotatedString {
        withStyle(style = SpanStyle(fontWeight = FontWeight.Thin)) {
            append(stringResource(id = R.string.thin_string))
        }
        withStyle(style = SpanStyle(fontWeight = FontWeight.Bold)) {
            append(stringResource(id = R.string.bold_string))
        }
    },
)

Depending on the style you assign, you will get a text with the style of the red highlight根据您分配的样式,您将获得带有红色突出显示样式的文本

https://i.stack.imgur.com/XSMMB.png https://i.stack.imgur.com/XSMMB.png

For more info: https://developer.android.com/jetpack/compose/text?hl=nl#multiple-styles更多信息: https : //developer.android.com/jetpack/compose/text?hl=nl#multiple-styles

There is ongoing discussion to implement on Jetpack Compose UI: https://issuetracker.google.com/issues/139320238正在进行关于在 Jetpack Compose UI 上实施的讨论: https : //issuetracker.google.com/issues/139320238

After some research, I came me up with the following solution that I also posted in the same discussion:经过一番研究,我想出了以下解决方案,我也在同一个讨论中发布了该解决方案:

@Composable
@ReadOnlyComposable
private fun resources(): Resources {
    LocalConfiguration.current
    return LocalContext.current.resources
}

fun Spanned.toHtmlWithoutParagraphs(): String {
    return HtmlCompat.toHtml(this, HtmlCompat.TO_HTML_PARAGRAPH_LINES_CONSECUTIVE)
        .substringAfter("<p dir=\"ltr\">").substringBeforeLast("</p>")
}

fun Resources.getText(@StringRes id: Int, vararg args: Any): CharSequence {
    val escapedArgs = args.map {
        if (it is Spanned) it.toHtmlWithoutParagraphs() else it
    }.toTypedArray()
    val resource = SpannedString(getText(id))
    val htmlResource = resource.toHtmlWithoutParagraphs()
    val formattedHtml = String.format(htmlResource, *escapedArgs)
    return HtmlCompat.fromHtml(formattedHtml, HtmlCompat.FROM_HTML_MODE_LEGACY)
}

@Composable
fun annotatedStringResource(@StringRes id: Int, vararg formatArgs: Any): AnnotatedString {
    val resources = resources()
    val density = LocalDensity.current
    return remember(id) {
        val text = resources.getText(id, *formatArgs)
        spannableStringToAnnotatedString(text, density)
    }
}

@Composable
fun annotatedStringResource(@StringRes id: Int): AnnotatedString {
    val resources = resources()
    val density = LocalDensity.current
    return remember(id) {
        val text = resources.getText(id)
        spannableStringToAnnotatedString(text, density)
    }
}

private fun spannableStringToAnnotatedString(
    text: CharSequence,
    density: Density
): AnnotatedString {
    return if (text is Spanned) {
        with(density) {
            buildAnnotatedString {
                append((text.toString()))
                text.getSpans(0, text.length, Any::class.java).forEach {
                    val start = text.getSpanStart(it)
                    val end = text.getSpanEnd(it)
                    when (it) {
                        is StyleSpan -> when (it.style) {
                            Typeface.NORMAL -> addStyle(
                                SpanStyle(
                                    fontWeight = FontWeight.Normal,
                                    fontStyle = FontStyle.Normal
                                ),
                                start,
                                end
                            )
                            Typeface.BOLD -> addStyle(
                                SpanStyle(
                                    fontWeight = FontWeight.Bold,
                                    fontStyle = FontStyle.Normal
                                ),
                                start,
                                end
                            )
                            Typeface.ITALIC -> addStyle(
                                SpanStyle(
                                    fontWeight = FontWeight.Normal,
                                    fontStyle = FontStyle.Italic
                                ),
                                start,
                                end
                            )
                            Typeface.BOLD_ITALIC -> addStyle(
                                SpanStyle(
                                    fontWeight = FontWeight.Bold,
                                    fontStyle = FontStyle.Italic
                                ),
                                start,
                                end
                            )
                        }
                        is TypefaceSpan -> addStyle(
                            SpanStyle(
                                fontFamily = when (it.family) {
                                    FontFamily.SansSerif.name -> FontFamily.SansSerif
                                    FontFamily.Serif.name -> FontFamily.Serif
                                    FontFamily.Monospace.name -> FontFamily.Monospace
                                    FontFamily.Cursive.name -> FontFamily.Cursive
                                    else -> FontFamily.Default
                                }
                            ),
                            start,
                            end
                        )
                        is BulletSpan -> {
                            Log.d("StringResources", "BulletSpan not supported yet")
                            addStyle(SpanStyle(), start, end)
                        }
                        is AbsoluteSizeSpan -> addStyle(
                            SpanStyle(fontSize = if (it.dip) it.size.dp.toSp() else it.size.toSp()),
                            start,
                            end
                        )
                        is RelativeSizeSpan -> addStyle(
                            SpanStyle(fontSize = it.sizeChange.em),
                            start,
                            end
                        )
                        is StrikethroughSpan -> addStyle(
                            SpanStyle(textDecoration = TextDecoration.LineThrough),
                            start,
                            end
                        )
                        is UnderlineSpan -> addStyle(
                            SpanStyle(textDecoration = TextDecoration.Underline),
                            start,
                            end
                        )
                        is SuperscriptSpan -> addStyle(
                            SpanStyle(baselineShift = BaselineShift.Superscript),
                            start,
                            end
                        )
                        is SubscriptSpan -> addStyle(
                            SpanStyle(baselineShift = BaselineShift.Subscript),
                            start,
                            end
                        )
                        is ForegroundColorSpan -> addStyle(
                            SpanStyle(color = Color(it.foregroundColor)),
                            start,
                            end
                        )
                        else -> addStyle(SpanStyle(), start, end)
                    }
                }
            }
        }
    } else {
        AnnotatedString(text.toString())
    }
}

Source: https://issuetracker.google.com/issues/139320238#comment11来源: https : //issuetracker.google.com/issues/139320238#comment11

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM