Java 如何在 Android 中将格式化字符串与占位符一起使用?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/23503642/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to use formatted strings together with placeholders in Android?
提问by maral
In Android it is possible to use placeholders in strings, such as:
在 Android 中,可以在字符串中使用占位符,例如:
<string name="number">My number is %1$d</string>
and then in Java code (inside a subclass of Activity
):
然后在 Java 代码中(在 的子类中Activity
):
String res = getString(R.string.number);
String formatted = String.format(res, 5);
or even simpler:
甚至更简单:
String formatted = getString(R.string.number, 5);
It is also possible to use some HTML tags in Android string resources:
也可以在 Android 字符串资源中使用一些 HTML 标签:
<string name="underline"><u>Underline</u> example</string>
Since the String
itself cannot hold any information about formatting, one should use getText(int)
instead of getString(int)
method:
由于String
本身不能保存任何有关格式的信息,因此应使用getText(int)
代替getString(int)
方法:
CharSequence formatted = getText(R.string.underline);
The returned CharSequence
can be then passed to Android widgets, such as TextView
, and the marked phrase will be underlined.
然后CharSequence
可以将返回的值传递给 Android 小部件,例如TextView
,标记的短语将带有下划线。
However, I could not find how to combine these two methodes, using formatted string together with placeholders:
但是,我找不到如何组合这两种方法,使用格式化字符串和占位符:
<string name="underlined_number">My number is <u>%1$d</u></string>
How to process above resource in the Java code to display it in a TextView
, substituting %1$d
with an integer?
如何在Java代码中处理上述资源以将其显示在a中TextView
,%1$d
用整数代替?
采纳答案by maral
Finally I managed to find a working solution and wrote my own method for replacing placeholders, preserving formatting:
最后我设法找到了一个可行的解决方案并编写了我自己的方法来替换占位符,保留格式:
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));
}
This approach does not require to escape HTML tags manually neither in a string being formatted nor in strings that replace placeholders.
这种方法不需要在格式化的字符串或替换占位符的字符串中手动转义 HTML 标签。
回答by Wagner Michael
<resources>
<string name="welcome_messages">Hello, %1$s! You have <b>%2$d new messages</b>.</string>
</resources>
Resources res = getResources();
String text = String.format(res.getString(R.string.welcome_messages), username, mailCount);
CharSequence styledText = Html.fromHtml(text);
More Infos here: http://developer.android.com/guide/topics/resources/string-resource.html
更多信息在这里:http: //developer.android.com/guide/topics/resources/string-resource.html
回答by Genma
For the simple case where you want to replace a placeholder without number formatting (i.e. leading zeros, numbers after comma) you can use Square Phraselibrary.
对于您想要替换没有数字格式的占位符(即前导零,逗号后的数字)的简单情况,您可以使用 Square Phrase库。
The usage is very simple: first you have to change the placeholders in your string resource to this simpler format:
用法非常简单:首先,您必须将字符串资源中的占位符更改为这种更简单的格式:
<string name="underlined_number">My number is <u> {number} </u></string>
then you can make the replacement like this:
那么你可以像这样进行替换:
CharSequence formatted = Phrase.from(getResources(), R.string.underlined_number)
.put("number", 5)
.format()
The formatted CharSequence
is also styled. If you need to format your numbers, you can always pre-format them using String.format("%03d", 5)
and then use the resulting string in the .put()
function.
格式化CharSequence
的也有样式。如果您需要格式化您的数字,您可以随时使用预先格式化它们String.format("%03d", 5)
,然后在.put()
函数中使用结果字符串。
回答by Chrispher
Similar to the accepted answer, I attempted to write a Kotlin extension method for this.
与接受的答案类似,我尝试为此编写 Kotlin 扩展方法。
Here's the accepted answer in Kotlin
这是 Kotlin 中公认的答案
@Suppress("DEPRECATION")
fun Context.getText(id: Int, vararg args: Any): CharSequence {
val escapedArgs = args.map {
if (it is String) TextUtils.htmlEncode(it) else it
}.toTypedArray()
return Html.fromHtml(String.format(Html.toHtml(SpannedString(getText(id))), *escapedArgs))
}
The problem with the accepted answer, is that it doesn't seem to work when the format arguments themselves are styled (i.e. Spanned, not String). By experiment, it seems to do weird things, possibly to do with the fact that we're not escaping non-String CharSequences. I'm seeing that if I call
接受的答案的问题在于,当格式参数本身被设置样式(即跨度,而不是字符串)时,它似乎不起作用。通过实验,它似乎做了一些奇怪的事情,可能与我们没有转义非 String CharSequences 的事实有关。我看到如果我打电话
context.getText(R.id.my_format_string, myHelloSpanned)
where R.id.my_format_string is:
其中 R.id.my_format_string 是:
<string name="my_format_string">===%1$s===</string>
and myHelloSpanned is a Spanned that looks like <b>hello</b>(i.e. it would have HTML <i><b>hello</b></i>
) then I get ===hello=== (i.e. HTML ===<b>hello</b>===
).
而 myHelloSpanned 是一个看起来像<b>hello</b>(即它会有 HTML <i><b>hello</b></i>
)的Spanned然后我得到 === hello=== (即 HTML ===<b>hello</b>===
)。
That is wrong, I should get ===<b>hello</b>===.
那是错误的,我应该得到 === <b>hello</b>===。
I tried to fix this by converting all CharSequences to HTML before applying String.format
, and here is my resulting code.
我试图通过在应用之前将所有 CharSequences 转换为 HTML 来解决这个问题String.format
,这是我的结果代码。
@Suppress("DEPRECATION")
fun Context.getText(@StringRes resId: Int, vararg formatArgs: Any): CharSequence {
// First, convert any styled Spanned back to HTML strings before applying String.format. This
// converts the styling to HTML and also does HTML escaping.
// For other CharSequences, just do HTML escaping.
// (Leave any other args alone.)
val htmlFormatArgs = formatArgs.map {
if (it is Spanned) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
Html.toHtml(it, Html.TO_HTML_PARAGRAPH_LINES_CONSECUTIVE)
} else {
Html.toHtml(it)
}
} else if (it is CharSequence) {
Html.escapeHtml(it)
} else {
it
}
}.toTypedArray()
// Next, get the format string, and do the same to that.
val formatString = getText(resId);
val htmlFormatString = if (formatString is Spanned) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
Html.toHtml(formatString, Html.TO_HTML_PARAGRAPH_LINES_CONSECUTIVE)
} else {
Html.toHtml(formatString)
}
} else {
Html.escapeHtml(formatString)
}
// Now apply the String.format
val htmlResultString = String.format(htmlFormatString, *htmlFormatArgs)
// Convert back to a CharSequence, recovering any of the HTML styling.
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
Html.fromHtml(htmlResultString, Html.FROM_HTML_MODE_LEGACY)
} else {
Html.fromHtml(htmlResultString)
}
}
However, this didn't quite work because when you call Html.toHtml
it puts <p>
tags around everything even when that extra padding wasn't in the input. Said another way, Html.fromHtml(Html.toHtml(myHelloSpanned))
is not equal to myHelloSpanned
- it's got extra padding. I didn't know how to resolve this nicely.
但是,这并不完全有效,因为当您调用Html.toHtml
它<p>
时,即使输入中没有额外的填充,它也会在所有内容周围放置标签。换句话说,Html.fromHtml(Html.toHtml(myHelloSpanned))
不等于myHelloSpanned
- 它有额外的填充。我不知道如何很好地解决这个问题。
回答by Frank Harper
Kotlin extension function that
Kotlin 扩展函数
- works with all API versions
- handles multiple arguments
- 适用于所有 API 版本
- 处理多个参数
Example usage
示例用法
textView.text = context.getText(R.string.html_formatted, "Hello in bold")
HTML string resource wrapped in a CDATA section
包含在 CDATA 部分中的 HTML 字符串资源
<string name="html_formatted"><![CDATA[ bold text: <B>%1$s</B>]]></string>
Result
结果
bold text: Hello in bold
粗体文字:你好,粗体
Code
代码
/**
* Create a formatted CharSequence from a string resource containing arguments and HTML formatting
*
* The string resource must be wrapped in a CDATA section so that the HTML formatting is conserved.
*
* Example of an HTML formatted string resource:
* <string name="html_formatted"><![CDATA[ bold text: <B>%1$s</B> ]]></string>
*/
fun Context.getText(@StringRes id: Int, vararg args: Any?): CharSequence {
val text = String.format(getString(id), *args)
return HtmlCompat.fromHtml(text, HtmlCompat.FROM_HTML_MODE_COMPACT)
}
回答by Sugandh Kumar
You can use java.lang.String for string formatting in Kotlin
您可以在 Kotlin 中使用 java.lang.String 进行字符串格式化
fun main(args : Array<String>) {
var value1 = 1
var value2 = "2"
var value3 = 3.0
println(java.lang.String.format("%d, %s, %6f", value1, value2, value3))
}
回答by Luke
Here's a more readable Kotlin extension which doesn't use deprecated APIs, works on all Android versions, and doesn't require strings to be wrapped in CDATA sections:
这是一个更具可读性的 Kotlin 扩展,它不使用已弃用的 API,适用于所有 Android 版本,并且不需要将字符串包装在 CDATA 部分中:
fun Context.getText(id: Int, vararg args: Any): CharSequence {
val escapedArgs = args.map {
if (it is String) TextUtils.htmlEncode(it) else it
}.toTypedArray()
val resource = SpannedString(getText(id))
val htmlResource = HtmlCompat.toHtml(resource, HtmlCompat.TO_HTML_PARAGRAPH_LINES_CONSECUTIVE)
val formattedHtml = String.format(htmlResource, *escapedArgs)
return HtmlCompat.fromHtml(formattedHtml, HtmlCompat.FROM_HTML_MODE_LEGACY)
}
You can add an alias as as extension of Fragment - just remember to spread the args in between:
您可以添加别名作为 Fragment 的扩展名 - 请记住将 args 分散在它们之间:
fun Fragment.getText(id: Int, vararg args: Any) = requireContext().getText(id, *args)