如何在Android中将格式化字符串与占位符一起使用?

mar*_*ral 37 java string android textview

在Android中,可以在字符串中使用占位符,例如:

<string name="number">My number is %1$d</string>
Run Code Online (Sandbox Code Playgroud)

然后在Java代码中(在子类内Activity):

String res = getString(R.string.number);
String formatted = String.format(res, 5);
Run Code Online (Sandbox Code Playgroud)

甚至更简单:

String formatted = getString(R.string.number, 5);
Run Code Online (Sandbox Code Playgroud)

也可以在Android字符串资源中使用一些HTML标记:

<string name="underline"><u>Underline</u> example</string>
Run Code Online (Sandbox Code Playgroud)

由于String本身无法保存有关格式化的任何信息,因此应使用getText(int)而不是getString(int)方法:

CharSequence formatted = getText(R.string.underline);
Run Code Online (Sandbox Code Playgroud)

然后CharSequence可以将返回的内容传递给Android小部件,例如TextView,标记的短语将加下划线.

但是,我无法找到如何将这两个方法组合在一起,使用格式化的字符串和占位符:

<string name="underlined_number">My number is <u>%1$d</u></string>
Run Code Online (Sandbox Code Playgroud)

如何在Java代码中处理上面的资源以在一个整数中TextView替换%1$d

mar*_*ral 24

最后,我设法找到了一个有效的解决方案并编写了自己的方法来替换占位符,保留了格式:

public static CharSequence getText(Context context, int id, Object... args) {
    for(int i = 0; i < args.length; ++i)
        args[i] = args[i] instanceof String? TextUtils.htmlEncode((String)args[i]) : args[i];
    return Html.fromHtml(String.format(Html.toHtml(new SpannedString(context.getText(id))), args));
}
Run Code Online (Sandbox Code Playgroud)

此方法不需要手动转义HTML标记,无论是在格式化的字符串中还是在替换占位符的字符串中.

  • 你还应该删除尾随的\n:```private static CharSequence removeTrailingLineFeed(@NonNull CharSequence text){while(text.charAt(text.length() - 1)=='\n'){text = text.subSequence (0,text.length() - 1); } return text; }``` (6认同)
  • @fernandospr 一定要检查你的 while() 条件中的 length() &gt; 0。 (2认同)
  • 如今,Kotlin 扩展可能更有意义。 (2认同)

Fra*_*per 19

Kotlin 扩展函数

  • 适用于所有 API 版本
  • 处理多个参数

示例用法

textView.text = context.getText(R.string.html_formatted, "Hello in bold")
Run Code Online (Sandbox Code Playgroud)

包含在 CDATA 部分中的 HTML 字符串资源

<string name="html_formatted"><![CDATA[ bold text: <B>%1$s</B>]]></string>
Run Code Online (Sandbox Code Playgroud)

结果

粗体文本:您好,粗体

代码

textView.text = context.getText(R.string.html_formatted, "Hello in bold")
Run Code Online (Sandbox Code Playgroud)


Wag*_*ael 16

<resources>
  <string name="welcome_messages">Hello, %1$s! You have &lt;b>%2$d new messages&lt;/b>.</string>
</resources>


Resources res = getResources();
String text = String.format(res.getString(R.string.welcome_messages), username, mailCount);
CharSequence styledText = Html.fromHtml(text);
Run Code Online (Sandbox Code Playgroud)

更多信息:http://developer.android.com/guide/topics/resources/string-resource.html


小智 8

对于您想要替换没有数字格式的占位符(即前导零,逗号后面的数字)的简单情况,您可以使用Square Phrase库.

用法非常简单:首先,您必须将字符串资源中的占位符更改为这种更简单的格式:

<string name="underlined_number">My number is <u> {number} </u></string>
Run Code Online (Sandbox Code Playgroud)

然后你可以像这样替换:

CharSequence formatted = Phrase.from(getResources(), R.string.underlined_number)
   .put("number", 5)
   .format()
Run Code Online (Sandbox Code Playgroud)

格式化CharSequence也是样式.如果需要格式化数字,可以随时使用它们进行预格式化String.format("%03d", 5),然后在.put()函数中使用结果字符串.


And*_*lly 7

这是最终对我有用的代码

字符串.xml

<string name="launch_awaiting_instructions">Contact <b>our</b> team on %1$s to activate.</string>
<string name="support_contact_phone_number"><b>555 555 555</b> Opt <b>3</b></string>
Run Code Online (Sandbox Code Playgroud)

科特林代码

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)
}
Run Code Online (Sandbox Code Playgroud)

使用这个我也能够使用样式占位符在 Android 上渲染样式文本

输出

请拨打555 555 555 Opt 3联系我们的团队以激活。

然后,我能够扩展此解决方案以创建以下 Compose 方法。

Jetpack 撰写 UI

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

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

private fun spannableStringToAnnotatedString(text: CharSequence): AnnotatedString {
    return 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())
    }
}
Run Code Online (Sandbox Code Playgroud)