簡體   English   中英

Android Jetpack Compose:如何顯示字符串資源中的樣式文本

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

我的strings.xml有一個字符串,它針對不同的語言進行了本地化。 字符串的樣式為每個本地化的 Html 標簽。

使用 Android TextView ,我能夠通過讀取字符串資源來很好地顯示樣式文本。

考慮到 Jetpack Compose 當前(1.0.0-rc02)不支持 Html 標簽,我嘗試在AndroidView可組合中使用TextView內的官方文檔: https : //developer.android.com/jetpack/compose/interop/interop-apis#組合視圖

我嘗試過的示例:

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

strings.xml文件中的文本:

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

但是,使用stringResource(id = R.string.styled_text)提供沒有Html 標簽的文本。

有沒有辦法在 Jetpack Compose 中使用 Html 樣式顯示字符串資源中的文本?


以下兩個問題類似,但從資源中讀取字符串:

Jetpack compose 在文本中顯示 html

Android Compose:如何在文本視圖中使用 HTML 標簽

stringResource下的stringResource使用resources.getString ,它丟棄任何樣式信息。 您需要創建類似textResource東西來獲取原始值:

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

並像這樣使用它:

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

目前,您可以使用AnnotatedString類在Text組件中顯示樣式測試。 我將向您展示一個示例:

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

根據您分配的樣式,您將獲得帶有紅色突出顯示樣式的文本

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

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

正在進行關於在 Jetpack Compose UI 上實施的討論: https : //issuetracker.google.com/issues/139320238

經過一番研究,我想出了以下解決方案,我也在同一個討論中發布了該解決方案:

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

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM