2011年3月19日土曜日

Android 複雑な文字列を xml で定義する

Android では、文字列を res/values/strings.xml の中に定義します。
*別に strings.xml という名前である必要はありません。stringsForScreen1.xml など任意のファイル名が使えます。

例えば、eclipse で Android プロジェクトを作成した場合、
デフォルトで作られる strings.xml は次のようになっています。


<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="hello">Hello World, MainActivity!</string>
<string name="app_name">HelloWorld</string>
</resources>


この文字列にアクセスするには、例えば、


<TextView
android:id="@+id/textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello"
/>





TextView textView = (TextView)findViewById(R.id.textview);
textView.setText(R.string.hello);

String hello = getString(R.string.hello);
String hello2 = (String) getText(R.string.hello);


のようにします。

ここまでが基本的な strings.xml の話です。


ここからが本題

文字列の一部をプログラムから指定できるようしてみます。

■ "本を◯◯冊買います" の場合


<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="buybooks">本を%1$d冊買います</string>
</resources>



String buybooks = getString(R.string.buybooks, 4);


%1$d の 1$ は getString() の2番目以降の引数を割り当てるときに、何番目の引数かを指定する番号です。
%d は数字, %s は文字列です。このあたりは Formatter で規定されています。


■ "☓☓を◯◯冊買います" の場合


<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="buyitems">%2$sを%1$d冊買います</string>
</resources>



String buybooks = getString(R.string.buybooks, 4, ”本");

String buybooks2 = String.format(getString(R.string.buybooks), 4 , "本");


1$, 2$ で順番を指定できると、多言語の文法に対応することができます。


<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="buyitems">Buy %1$d %2$s</string>
</resources>



String buybooks = getString(R.string.buybooks, 4, books);

String buybooks2 = String.format(getString(R.string.buybooks), 4 , books);



■ 文法の複数形に対応する - plurals -

さて、英語の場合、次の書き方では問題があります。


<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="buybooks">Buy %1$d books</string>
</resources>


買う本が1冊の場合、文法的には "Buy 1 book" が正しいからです。

このように、単数形と複数形の違いに対応するために plurals が用意されています。

res/values/ の下の任意の xml ファイルで plurals を定義します。

例えば、
res/values/plurals.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
<plurals name="plural_book">
<item quantity="one">Buy one book</item>
<item quantity="other">Buy %1$d books</item>
</plurals>
</resources>


quantity に設定できる値は "zero" | "one" | "two" | "few" | "many" | "other" です。
item の値の形式は string と同じ


String buy1book = getResources().getQuantityString(R.plurals.buybooks, 1);
String buynbooks = getResources().getQuantityString(R.plurals.buybooks, 4, 4);


getQuantityString(int id, int qunatity)getQuantityString(int id, int quantity, Object... fromatArgs) で取得する。

■ HTML markup でスタイリング

xml で定義する文字列には、<b>bold text</b>, <i>italic text</i>, <u>underline text<u> が使えます。
例えば、

<string name="hello">Hello World, <b>MainActivity!</b></string>


ただし、この場合 getText(int resId) での取得はエラーになります。


format + HTML markup の場合

<string name="buybooksbold">本を<b>%1$d</b>冊買います</string>



String buybooksbold = getString(R.string.buybooksbold, 4);

TextView textView = (TextView)findViewById(R.id.textview);
textView.setText(buybooksbold);


と書いても、太字になりません。
format されるときに、文字列のすべてのスタイル情報を取り除いてしまうからです。

そのため、まず、html エスケープな文字列として xml に定義し、format したあとで fromHtml(String) メソッドを使って HTML text をスタイルします。


<string name="buybooksbold">本を&lt;b>%1$d&lt;/b>冊買います</string>



String buybooksbold = getString(R.string.buybooksbold, 4);

CharSequence styledText = Html.fromHtml(buybooksbold);

TextView textView = (TextView)findViewById(R.id.textview);
textView.setText(styledText);


Document には、
”fromHtml(String) はすべての HTML entity に対して適応されてしまいますので、format で挿入する文字列も htmlEncode(String) でエスケープする必要があります。”
と書いてあるんだけど、エスケープしなくてもいけたのでよくわからない。
ちなみにエスケープするなら、こんな感じになります。


<string name="buyitemsbold">&lt;b>%2$s&lt;/b>を&lt;b>%1$d&lt;/b>冊買います</string>



String buyitemsbold = getString(R.string.buyitemsbold, 4, TextUtils.htmlEncode("本"));

CharSequence styledText = Html.fromHtml(buyitemsbold);

TextView textView = (TextView)findViewById(R.id.textview);
textView.setText(styledText);



■ 注意 : xml で定義する文字列に ' が含まれている場合、エスケープが必要

これはOK

<string name="good_example">"This'll work"</string>
<string name="good_example_2">This\'ll also work</string>


これはダメ

<string name="bad_example">This doesn't work</string>
<string name="bad_example_2">XML encodings don't work</string>



String Resources
public final String getString(int resId)
public final CharSequence getText(int resId)



 

0 件のコメント:

コメントを投稿