2011年3月19日土曜日

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

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

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

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


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

  1. <TextView  
  2.     android:id="@+id/textview"  
  3.     android:layout_width="wrap_content"  
  4.     android:layout_height="wrap_content"  
  5.     android:text="@string/hello"  
  6.     />  




  1. TextView textView = (TextView)findViewById(R.id.textview);  
  2. textView.setText(R.string.hello);  
  3.   
  4. String hello = getString(R.string.hello);  
  5. String hello2 = (String) getText(R.string.hello);  


のようにします。

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


ここからが本題

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

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

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


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


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


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

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


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


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

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


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



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

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

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


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

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

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

例えば、
res/values/plurals.xml
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <resources>  
  3.     <plurals name="plural_book">  
  4.         <item quantity="one">Buy one book</item>  
  5.         <item quantity="other">Buy %1$d books</item>  
  6.     </plurals>  
  7. </resources>  


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

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


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> が使えます。
例えば、
  1. <string name="hello">Hello World, <b>MainActivity!</b></string>  


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


format + HTML markup の場合
  1. <string name="buybooksbold">本を<b>%1$d</b>冊買います</string>  


  1. String buybooksbold = getString(R.string.buybooksbold, 4);  
  2.   
  3. TextView textView = (TextView)findViewById(R.id.textview);  
  4. textView.setText(buybooksbold);  


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

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

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


  1. String buybooksbold = getString(R.string.buybooksbold, 4);  
  2.   
  3. CharSequence styledText = Html.fromHtml(buybooksbold);          
  4.   
  5. TextView textView = (TextView)findViewById(R.id.textview);  
  6. textView.setText(styledText);  


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

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


  1. String buyitemsbold = getString(R.string.buyitemsbold, 4, TextUtils.htmlEncode("本"));  
  2.   
  3. CharSequence styledText = Html.fromHtml(buyitemsbold);          
  4.   
  5. TextView textView = (TextView)findViewById(R.id.textview);  
  6. textView.setText(styledText);  



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

これはOK
  1. <string name="good_example">"This'll work"</string>  
  2. <string name="good_example_2">This\'ll also work</string>  


これはダメ
  1. <string name="bad_example">This doesn't work</string>  
  2. <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 件のコメント:

コメントを投稿