Androidでは、次のような文字列でプレースホルダーを使用することが可能です:
_<string name="number">My number is %1$d</string>
_
そしてJavaコード(Activity
のサブクラス内):
_String res = getString(R.string.number);
String formatted = String.format(res, 5);
_
またはさらに簡単:
_String formatted = getString(R.string.number, 5);
_
Android文字列リソースでいくつかのHTMLタグを使用することも可能です:
_<string name="underline"><u>Underline</u> example</string>
_
String
自体はフォーマットに関する情報を保持できないため、getText(int)
メソッドの代わりにgetString(int)
を使用する必要があります。
_CharSequence formatted = getText(R.string.underline);
_
返されたCharSequence
は、Androidウィジェット(TextView
など)に渡すことができ、マークされたフレーズには下線が付きます。
ただし、フォーマットされた文字列とプレースホルダーを使用して、これら2つの方法を組み合わせる方法を見つけることができませんでした。
_<string name="underlined_number">My number is <u>%1$d</u></string>
_
上記のリソースをJavaコードで処理してTextView
に表示し、_%1$d
_を整数に置き換えますか?
最後に、実用的なソリューションを見つけて、プレースホルダーを置き換えるための独自の方法を記述し、フォーマットを維持しました。
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));
}
このアプローチでは、フォーマットされている文字列内でも、プレースホルダーを置き換える文字列内でも、手動でHTMLタグをエスケープする必要はありません。
<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);
詳細はこちら: http://developer.Android.com/guide/topics/resources/string-resource.html
数値フォーマットなしのプレースホルダー(つまり、先行ゼロ、コンマの後の数値)を置き換える単純なケースでは、Square Phrase ライブラリーを使用できます。
使用方法は非常に簡単です。最初に、文字列リソースのプレースホルダーを次の簡単な形式に変更する必要があります。
_<string name="underlined_number">My number is <u> {number} </u></string>
_
次に、このように置き換えることができます:
_CharSequence formatted = Phrase.from(getResources(), R.string.underlined_number)
.put("number", 5)
.format()
_
フォーマットされたCharSequence
もスタイル設定されます。数値をフォーマットする必要がある場合は、いつでもString.format("%03d", 5)
を使用して数値を事前フォーマットし、その結果の文字列を.put()
関数で使用できます。
textView.text = context.getText(R.string.html_formatted, "Hello in bold")
<string name="html_formatted"><![CDATA[ bold text: <B>%1$s</B>]]></string>
太字:こんにちは太字
/**
* 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 if (Android.os.Build.VERSION.SDK_INT >= 24)
Html.fromHtml(text, Html.FROM_HTML_MODE_COMPACT)
else
Html.fromHtml(text)
}
受け入れられた回答と同様に、私はこのために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))
}
_
受け入れられた回答の問題は、フォーマット引数自体がスタイル設定されている(つまり、ストリングではなくスパンされている)場合に機能しないように見えることです。実験的に、奇妙なことをしているようです。おそらく、文字列以外のCharSequenceをエスケープしていないという事実に関係しています。電話すると
_context.getText(R.id.my_format_string, myHelloSpanned)
_
ここで、R.id.my_format_stringは次のとおりです。
_<string name="my_format_string">===%1$s===</string>
_
そしてmyHelloSpannedは<b> hello </ b>のように見えるスパンドです(つまり、HTML _<i><b>hello</b></i>
_になるでしょう)= == hello===(つまり、HTML _===<b>hello</b>===
_)。
それは間違っています、===<b> hello </ b>===を取得する必要があります。
_String.format
_を適用する前にすべてのCharSequencesをHTMLに変換することでこれを修正しようとしましたが、これが結果のコードです。
_@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)
}
}
_
ただし、_Html.toHtml
_を呼び出すと、入力に余分なパディングがない場合でも、_<p>
_タグがすべての周りに配置されるため、これはうまくいきませんでした。別の言い方をすると、Html.fromHtml(Html.toHtml(myHelloSpanned))
はmyHelloSpanned
と等しくありません-追加のパディングがあります。これをうまく解決する方法を知りませんでした。
廃止されたAPIを使用せず、すべてのAndroid=バージョンで機能し、CDATAセクションで文字列をラップする必要がない、より読みやすいKotlin拡張機能を次に示します。
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)
}
フラグメントの拡張としてエイリアスを追加できます-間に引数を分散することを忘れないでください:
fun Fragment.getText(id: Int, vararg args: Any) = requireContext().getText(id, *args)