文字列リソースは、アプリのテキスト文字列に対して、テキストのスタイルや書式を任意で設定します。アプリに文字列を提供できるリソースは 3 種類あります。
- 文字列
- 1 つの文字列を提供する XML リソース。
- 文字列配列
- 文字列配列を提供する XML リソース。
- 数量文字列(複数形)
- さまざまな複数形の文字列を扱う XML リソース。
すべての文字列は、スタイル設定のマークアップや書式設定の引数を適用可能です。文字列のスタイル設定と書式設定の詳細は、書式設定とスタイル設定のセクションをご覧ください。
文字列
アプリから、または他のリソース ファイル(XML レイアウトなど)から参照できる、1 つの文字列。
注:文字列は、name
属性(XML ファイルの名前ではありません)で提供された値を使って参照される、単純なリソースです。そのため、文字列リソースと他の単純なリソースを、1 つの XML ファイル内の 1 つの <resources>
要素の中で組み合わせることが可能です。
- ファイルの場所:
res/values/filename.xml
ファイル名は任意です。<string>
要素のname
はリソース ID として使用されます。- コンパイルされるリソースのデータ型:
String
へのリソース ポインタ。- リソースの参照:
-
Java の場合:
R.string.string_name
XML の場合:@string/string_name
- 構文:
-
<?xml version="1.0" encoding="utf-8"?> <resources> <string name="string_name" >text_string</string> </resources>
- エレメント:
- 例:
res/values/strings.xml
に保存された XML ファイル:<?xml version="1.0" encoding="utf-8"?> <resources> <string name="hello">Hello!</string> </resources>
このレイアウト XML は文字列をビューに適用します。
<TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/hello" />
このアプリのコードは文字列を取得します。
getString(int)
とgetText(int)
のどちらかを使って文字列を取得できます。getText(int)
は文字列に適用されたリッチテキストのスタイルを保持します。
文字列配列
アプリから参照可能な文字列配列。
注:文字列配列は、name
属性(XML ファイルの名前ではありません)で提供された値を使って参照される、単純なリソースです。そのため、文字列配列リソースと他の単純なリソースを、1 つの XML ファイル内の 1 つの <resources>
要素の中で組み合わせることが可能です。
- ファイルの場所:
res/values/filename.xml
ファイル名は任意です。<string-array>
要素のname
はリソース ID として使用されます。- コンパイルされるリソースのデータ型:
String
配列へのリソース ポインタ。- リソースの参照:
-
Java の場合:
R.array.string_array_name
- 構文:
-
<?xml version="1.0" encoding="utf-8"?> <resources> <string-array name="string_array_name"> <item >text_string</item> </string-array> </resources>
- エレメント:
- 例:
res/values/strings.xml
に保存された XML ファイル:<?xml version="1.0" encoding="utf-8"?> <resources> <string-array name="planets_array"> <item>Mercury</item> <item>Venus</item> <item>Earth</item> <item>Mars</item> </string-array> </resources>
このアプリのコードは文字列配列を取得します。
Kotlin
val array: Array
= resources
.getStringArray
(R.array.planets_array)Java
Resources res =
getResources()
; String[] planets = res.getStringArray
(R.array.planets_array);
数量文字列(複数形)
言語が異なると、数量と文法を一致させるためのルールも異なります。たとえば、英語では、1 という数は特別なケースです。1 冊の本は「1 book」と書きますが、その他の数の場合は「n books」と書きます。この単数形と複数形の違いはごく一般的なものですが、もっと微妙な違いのある言語も存在します。Android でサポートされているフルセットは、zero
、one
、two
、few
、many
、other
です。
特定の言語と数量で使用すべきケースを判断するためのルールは、非常に複雑になりかねないので、Android では getQuantityString()
という、適切なリソースを選択するためのメソッドが用意されています。
以前から「数量文字列」と呼ばれていますが(API 内でもそう呼ばれています)、数量文字列は複数形にのみ使用します。たとえば、Gmail の「Inbox」と、未読メッセージがある場合の「Inbox (12)」のような違いを実装するために数量文字列を使うのは誤りです。if
文の代わりに数量文字列を使うのは便利に思えるかもしれませんが、一部の言語(中国語など)ではそうした文法上の区別をしないため、常に other
文字列を使うという点に注意が必要です。
使用する文字列の選択は、文法上の「必要性」にのみ基づいて行われます。英語では、zero
の文字列は、数量が 0 の場合でも無視されます。0 は、1 以外の、2 やその他の数と文法上の違いはないからです(「zero books」、「one book」、「two books」などと表します)。反対に韓国語では、other
文字列のみが使われます。
たとえば、two
は 2 という数量にのみ適用できるように思えますが、誤解のないようにしてください。ある言語では、2、12、102 などの数はすべて同じように扱い、他の数とは区別する必要があるかもしれないからです。各言語が実際にどのような区別を必要としているかを知るには、その言語の翻訳担当者に相談してください。
たとえば「Books: 1」のように、数量について中立的な表現を使うことで、数量文字列を使用せずに済む場合が多くあります。それがアプリで許容できるスタイルであれば、デベロッパーや翻訳者の負担は減ります。
注:複数形の集合は、name
属性(XML ファイルの名前ではありません)で提供された値を使って参照される、単純なリソースです。そのため、複数形リソースと他の単純なリソースを、1 つの XML ファイル内の 1 つの <resources>
要素の中で組み合わせることが可能です。
- ファイルの場所:
res/values/filename.xml
ファイル名は任意です。<plurals>
要素のname
はリソース ID として使用されます。- リソースの参照:
-
Java の場合:
R.plurals.plural_name
- 構文:
-
<?xml version="1.0" encoding="utf-8"?> <resources> <plurals name="plural_name"> <item quantity=["zero" | "one" | "two" | "few" | "many" | "other"] >text_string</item> </plurals> </resources>
- エレメント:
- 例:
res/values/strings.xml
に保存された XML ファイル:<?xml version="1.0" encoding="utf-8"?> <resources> <plurals name="numberOfSongsAvailable"> <!-- As a developer, you should always supply "one" and "other" strings. Your translators will know which strings are actually needed for their language. Always include %d in "one" because translators will need to use %d for languages where "one" doesn't mean 1 (as explained above). --> <item quantity="one">%d song found.</item> <item quantity="other">%d songs found.</item> </plurals> </resources>
res/values-pl/strings.xml
に保存された XML ファイル:<?xml version="1.0" encoding="utf-8"?> <resources> <plurals name="numberOfSongsAvailable"> <item quantity="one">Znaleziono %d piosenkę.</item> <item quantity="few">Znaleziono %d piosenki.</item> <item quantity="other">Znaleziono %d piosenek.</item> </plurals> </resources>
使用方法:
Kotlin
val count = getNumberOfSongsAvailable() val songsFound = resources.
getQuantityString
(R.plurals.numberOfSongsAvailable, count, count)Java
int count = getNumberOfSongsAvailable(); Resources res =
getResources()
; String songsFound = res.getQuantityString
(R.plurals.numberOfSongsAvailable, count, count);getQuantityString()
メソッドを使う場合、文字列に数による文字列書式設定が含まれていれば、count
を 2 回渡す必要があります。たとえば、%d songs found
という文字列では、1 回目のcount
パラメータが適切な複数形の文字列を選び、2 回目のcount
パラメータが%d
プレースホルダに挿入されます。複数形の文字列に文字列書式設定が含まれていなければ、3 つ目のパラメータをgetQuantityString
に渡す必要はありません。
書式設定とスタイル設定
ここでは、文字列リソースの書式とスタイルを正しく設定するために理解しておくべき重要な点をいくつか説明します。
特殊文字を処理する
XML または Android で特別な用途を持つ文字が文字列に含まれている場合は、その文字をエスケープする必要があります。一部の文字は先頭にバックスラッシュを付けることでエスケープできますが、他の文字は XML でのエスケープ処理が必要となります。アポストロフィと一重引用符は、文字列全体を二重引用符で囲むことによっても処理できます。いくつかの例を以下に示します。
文字 | エスケープされた書式 |
---|---|
@ | \@ |
? | \? |
< | < |
& | & |
一重引用符(' ) |
次のいずれかになります。
|
二重引用符(" ) |
次のいずれかになります。
二重引用符をエスケープする必要があります。文字列を一重引用符で囲むと機能しません。 |
文字列の書式を設定する
文字列の書式を設定する必要がある場合は、次の例のリソースに示すように、書式引数を文字列リソースに格納します。
<string name="welcome_messages">Hello, %1$s! You have %2$d new messages.</string>
この例では、書式文字列には 2 つの引数があります。%1$s
は文字列、%2$d
は小数です。次に、getString(int, Object...)
を呼び出して文字列の書式を設定します。次に例を示します。
Kotlin
var text = getString(R.string.welcome_messages, username, mailCount)
Java
String text = getString(R.string.welcome_messages, username, mailCount);
HTML マークアップでスタイルを設定する
HTML マークアップで文字列にスタイル設定を追加できます。次に例を示します。
<?xml version="1.0" encoding="utf-8"?> <resources> <string name="welcome">Welcome to <b>Android</b>!</string> </resources>
次の HTML 要素がサポートされています。
- 太字: <b>、<em>
- 斜体: <i>、<cite>、<dfn>
- テキストを 25% 拡大: <big>
- テキストを 20% 縮小: <small>
- フォント プロパティの設定: <font face=”font_family“ color=”hex_color”>。考えられるフォント ファミリーの例には、
monospace
、serif
、sans_serif
などがあります。 - 固定幅フォント ファミリーの設定: <tt>
- 取り消し線: <s>、<strike>、<del>
- 下線: <u>
- 上付き文字: <sup>
- 下付き文字: <sub>
- 箇条書き: <ul>、<li>
- 改行: <br>
- 区分: <div>
- CSS スタイル: <span style=”color|background_color|text-decoration”>
- 段落: <p dir=”rtl | ltr” style=”…”>
書式設定を適用していない場合は、setText(java.lang.CharSequence)
を呼び出して TextView テキストを直接設定できます。ただし、場合によっては、書式文字列としても使用するスタイル設定済みのテキスト リソースを作成することがあります。format(String, Object...)
メソッドと getString(int, Object...)
メソッドは文字列からすべてのスタイル情報を削除するため、通常これは機能しません。この回避策は、エスケープされたエンティティを使って HTML タグを書くことです。そうすると、書式設定が行われた後に、fromHtml(String)
によって復元されます。次に例を示します。
- スタイル設定をしたテキスト リソースを HTML のエスケープ処理をした文字列として格納します。
<resources> <string name="welcome_messages">Hello, %1$s! You have <b>%2$d new messages</b>.</string> </resources>
この書式設定済みの文字列には、
<b>
要素が追加されています。左かっこは、<
記号を使って HTML をエスケープしていることに注意してください。 - 次に、通常の方法で文字列の書式を設定しますが、さらに
fromHtml(String)
を呼び出して、HTML テキストをスタイル設定済みのテキストに変換します。Kotlin
val text: String = getString(R.string.welcome_messages, username, mailCount) val styledText: Spanned = Html.fromHtml(text, FROM_HTML_MODE_LEGACY)
Java
String text = getString(R.string.welcome_messages, username, mailCount); Spanned styledText = Html.fromHtml(text, FROM_HTML_MODE_LEGACY);
fromHtml(String)
メソッドでは、すべての HTML エンティティの書式を設定します。そのため、htmlEncode(String)
を使用して、書式設定済みのテキストで使用している文字列中の HTML 文字をできる限りエスケープするようにします。たとえば、「<」や「&」のような文字を含む文字列の書式を設定する場合には、書式を設定する前にそうした文字をエスケープする必要があります。そうすると、書式設定済みの文字列が fromHtml(String)
によって渡されたときに、その文字は本来書かれていたとおりに表示されます。次に例を示します。
Kotlin
val escapedUsername: String = TextUtils.htmlEncode
(username)
val text: String = getString(R.string.welcome_messages, escapedUsername, mailCount)
val styledText: Spanned = Html.fromHtml(text, FROM_HTML_MODE_LEGACY)
Java
String escapedUsername = TextUtils.htmlEncode
(username);
String text = getString(R.string.welcome_messages, escapedUsername, mailCount);
Spanned styledText = Html.fromHtml(text);
Spannable でスタイルを設定する
Spannable
は、色やフォント ウェイトといった書体プロパティでスタイルを設定できるテキスト オブジェクトです。SpannableStringBuilder
を使ってテキストを作成してから、android.text.style
パッケージで定義されたスタイルをそのテキストに適用します。
次のヘルパー メソッドを使うと、Spannable テキストを作成する処理の大部分を構成できます。
Kotlin
/** * Returns a CharSequence that concatenates the specified array of CharSequence * objects and then applies a list of zero or more tags to the entire range. * * @param content an array of character sequences to apply a style to * @param tags the styled span objects to apply to the content * such as android.text.style.StyleSpan */ private fun apply(content: Array<out CharSequence>, vararg tags: Any): CharSequence { return SpannableStringBuilder().apply { openTags(tags) content.forEach { charSequence -> append(charSequence) } closeTags(tags) } } /** * Iterates over an array of tags and applies them to the beginning of the specified * Spannable object so that future text appended to the text will have the styling * applied to it. Do not call this method directly. */ private fun Spannable.openTags(tags: Array<out Any>) { tags.forEach { tag -> setSpan(tag, 0, 0, Spannable.SPAN_MARK_MARK) } } /** * "Closes" the specified tags on a Spannable by updating the spans to be * endpoint-exclusive so that future text appended to the end will not take * on the same styling. Do not call this method directly. */ private fun Spannable.closeTags(tags: Array<out Any>) { tags.forEach { tag -> if (length > 0) { setSpan(tag, 0, length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) } else { removeSpan(tag) } } }
Java
/** * Returns a CharSequence that concatenates the specified array of CharSequence * objects and then applies a list of zero or more tags to the entire range. * * @param content an array of character sequences to apply a style to * @param tags the styled span objects to apply to the content * such as android.text.style.StyleSpan * */ private static CharSequence applyStyles(CharSequence[] content, Object[] tags) { SpannableStringBuilder text = new SpannableStringBuilder(); openTags(text, tags); for (CharSequence item : content) { text.append(item); } closeTags(text, tags); return text; } /** * Iterates over an array of tags and applies them to the beginning of the specified * Spannable object so that future text appended to the text will have the styling * applied to it. Do not call this method directly. */ private static void openTags(Spannable text, Object[] tags) { for (Object tag : tags) { text.setSpan(tag, 0, 0, Spannable.SPAN_MARK_MARK); } } /** * "Closes" the specified tags on a Spannable by updating the spans to be * endpoint-exclusive so that future text appended to the end will not take * on the same styling. Do not call this method directly. */ private static void closeTags(Spannable text, Object[] tags) { int len = text.length(); for (Object tag : tags) { if (len > 0) { text.setSpan(tag, 0, len, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } else { text.removeSpan(tag); } } }
次の bold
、italic
、color
のメソッドでは、前述のヘルパー メソッドをラップして、android.text.style
パッケージで定義されたスタイルを適用する具体例を示しています。別の種類のテキスト スタイル設定を行う場合にも、同じメソッドを作成できます。
Kotlin
/** * Returns a CharSequence that applies boldface to the concatenation * of the specified CharSequence objects. */ fun bold(vararg content: CharSequence): CharSequence = apply(content, StyleSpan(Typeface.BOLD)) /** * Returns a CharSequence that applies italics to the concatenation * of the specified CharSequence objects. */ fun italic(vararg content: CharSequence): CharSequence = apply(content, StyleSpan(Typeface.ITALIC)) /** * Returns a CharSequence that applies a foreground color to the * concatenation of the specified CharSequence objects. */ fun color(color: Int, vararg content: CharSequence): CharSequence = apply(content, ForegroundColorSpan(color))
Java
/** * Returns a CharSequence that applies boldface to the concatenation * of the specified CharSequence objects. */ public static CharSequence bold(CharSequence... content) { return apply(content, new StyleSpan(Typeface.BOLD)); } /** * Returns a CharSequence that applies italics to the concatenation * of the specified CharSequence objects. */ public static CharSequence italic(CharSequence... content) { return apply(content, new StyleSpan(Typeface.ITALIC)); } /** * Returns a CharSequence that applies a foreground color to the * concatenation of the specified CharSequence objects. */ public static CharSequence color(int color, CharSequence... content) { return apply(content, new ForegroundColorSpan(color)); }
次の例では、こうしたメソッドを連結させて、1 つのフレーズ内の個々の単語にさまざまなスタイルを適用する方法を示しています。
Kotlin
// Create an italic "hello, " a red "world", // and bold the entire sequence. val text: CharSequence = bold(italic(getString(R.string.hello)), color(Color.RED, getString(R.string.world)))
Java
// Create an italic "hello, " a red "world", // and bold the entire sequence. var text = bold(italic(getString(R.string.hello)), color(Color.RED, getString(R.string.world))) </pre> </section><section><h3 id="java">Java</h3> <pre class="prettyprint lang-java"> // Create an italic "hello, " a red "world", // and bold the entire sequence. CharSequence text = bold(italic(getString(R.string.hello)), color(Color.RED, getString(R.string.world)));
core-ktx Kotlin モジュールには、スパンの操作をさらに容易にする拡張機能も含まれています。詳細については、GitHub の android.text パッケージのドキュメントをご覧ください。
スパンの操作の詳細については、次のリンクをご覧ください。
アノテーションでスタイルを設定する
Annotation
クラスと strings.xml リソース ファイルの <annotation>
タグを併用することで、複雑なスタイル設定またはカスタム スタイル設定を適用できます。アノテーション タグを使用すると、カスタムのキーと値のペアを XML で定義することで、カスタム スタイルを設定する文字列内の部分にマークを付けることができます。その後、フレームワークが Annotation
スパンに変換します。次に、これらのアノテーションを取得し、キーと値を使用してスタイル設定を適用できます。
アノテーションを作成するときは、各 strings.xml ファイル内の文字列のすべての翻訳に <annotation>
タグを必ず追加してください。
すべての言語の「テキスト」という単語にカスタム書体を適用
例 - カスタム書体の追加
-
<annotation>
タグを追加して、キーと値のペアを定義します。この場合、キーは font で、値は使用するフォントの種類 title_emphasis です。// values/strings.xml <string name="title">Best practices for <annotation font="title_emphasis">text</annotation> on Android</string> // values-es/strings.xml <string name="title"><annotation font="title_emphasis">Texto</annotation> en Android: mejores prácticas</string>
-
文字列リソースを読み込み、font キーを使用してアノテーションを見つけます。次に、カスタムスパンを作成して、既存のスパンを置き換えます。
Kotlin
// get the text as SpannedString so we can get the spans attached to the text val titleText = getText(R.string.title) as SpannedString // get all the annotation spans from the text val annotations = titleText.getSpans(0, titleText.length, Annotation::class.java) // create a copy of the title text as a SpannableString. // the constructor copies both the text and the spans. so we can add and remove spans val spannableString = SpannableString(titleText) // iterate through all the annotation spans for (annotation in annotations) { // look for the span with the key font if (annotation.key == "font") { val fontName = annotation.value // check the value associated to the annotation key if (fontName == "title_emphasis") { // create the typeface val typeface = getFontCompat(R.font.permanent_marker) // set the span at the same indices as the annotation spannableString.setSpan(CustomTypefaceSpan(typeface), titleText.getSpanStart(annotation), titleText.getSpanEnd(annotation), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) } } } // now, the spannableString contains both the annotation spans and the CustomTypefaceSpan styledText.text = spannableString
Java
// get the text as SpannedString so we can get the spans attached to the text SpannedString titleText = (SpannedString) getText(R.string.title_about); // get all the annotation spans from the text Annotation[] annotations = titleText.getSpans(0, titleText.length(), Annotation.class); // create a copy of the title text as a SpannableString. // the constructor copies both the text and the spans. so we can add and remove spans SpannableString spannableString = new SpannableString(titleText); // iterate through all the annotation spans for (Annotation annotation: annotations) { // look for the span with the key font if (annotation.getKey().equals("font")) { String fontName = annotation.getValue(); // check the value associated to the annotation key if (fontName.equals("title_emphasis")) { // create the typeface Typeface typeface = ResourcesCompat.getFont(this, R.font.roboto_mono); // set the span at the same indices as the annotation spannableString.setSpan(new CustomTypefaceSpan(typeface), titleText.getSpanStart(annotation), titleText.getSpanEnd(annotation), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } } } // now, the spannableString contains both the annotation spans and the CustomTypefaceSpan styledText.text = spannableString;
同じテキストを複数回使用する場合は、SpannableString オブジェクトを 1 回作成し、必要に応じて再利用して、パフォーマンスとメモリの問題を回避する必要があります。
アノテーションの使用方法のその他の例については、Android における国際化テキストのスタイル設定をご覧ください。
アノテーション スパンとテキストのパーセル化
Annotation
スパンも ParcelableSpans
であるため、キーと値のペアはパーセル化およびパーセル化解除されます。パーセルのレシーバーがアノテーションを解釈する方法を知っている場合は、Annotation
スパンを使用して、パーセル化されたテキストにカスタム スタイル設定を適用できます。
テキストをインテント バンドルに渡すときにカスタム スタイル設定を維持するには、最初に Annotation
スパンをテキストに追加する必要があります。これは、上記の例のように <annotation> タグを使用して XML リソースで行うか、以下のようにコードで新しい Annotation
を作成してスパンとして設定することで行います。
Kotlin
val spannableString = SpannableString("My spantastic text") val annotation = Annotation("font", "title_emphasis") spannableString.setSpan(annotation, 3, 7, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) // start Activity with text with spans val intent = Intent(this, MainActivity::class.java) intent.putExtra(TEXT_EXTRA, spannableString) startActivity(intent)
Java
SpannableString spannableString = new SpannableString("My spantastic text"); Annotation annotation = new Annotation("font", "title_emphasis"); spannableString.setSpan(annotation, 3, 7, 33); // start Activity with text with spans Intent intent = new Intent(this, MainActivity.class); intent.putExtra(TEXT_EXTRA, spannableString); this.startActivity(intent);
上記の例のように、テキストを Bundle
から SpannableString
として取得し、添付されているアノテーションを解析します。
Kotlin
// read text with Spans val intentCharSequence = intent.getCharSequenceExtra(TEXT_EXTRA) as SpannableString
Java
// read text with Spans SpannableString intentCharSequence = (SpannableString)intent.getCharSequenceExtra(TEXT_EXTRA);
テキストのスタイル設定の詳細については、次のリンクをご覧ください。