プログラマーのメモ書き

伊勢在住のプログラマーが気になることを気ままにメモったブログです

【Android】TextViewでHtmlスタイルの文字列を表示する

型番リーダ』のヘルプを作成した際に、TextViewでHtmlでスタイル付けする方法を調べたので、まとめておきます。android 1.6で検証しました。

 

1.やり方

参考先に 詳しいので、簡単に書いておくと、

 

String str = "<u>Hello World, TextViewHtmlTest!<u>";
CharSequence cs = Html.fromHtml(str);
txt1.setText(cs);

のように、Htmlクラスの静的メソッド、fromHtmlを使えば、htmlを解釈してスタイル 付きで表示してくれます。

 

(参考にしたサイト)

TextView を使いこなそう 縲鰀 表示編 縲怐@ その2

 

なお、アンカータグ <a> やイメージタグ <img> は下記サイトで示すような措置も必要になります(ここでは触れません)。

TextView を使いこなそう 縲鰀 表示編 縲怐@ その3

Is it possible to display inline images from html in an Android TextView? (英語)

Html.ImageGetter (英語)

 

2.注意点

上記のコードを実行する簡単なサンプルを作ってみるとこれでうまくいきます。しかし、実際にアプリで使おうとすると、TextViewの表示結果にhtmlが反映されていないことがあります。なぜでしょうか?

調べてみると、htmlタグを含む文字列をそのままリソースとして定義した場合に起きていました。

これは、リソースIDから文字列を取得するgetStringを呼び出すと、その際にhtmlタグが取り除かれてしまうことが原因です。

従って、これを回避するには、リソース定義のところでタグの文字をエスケープ文字に代えればokです。

実験

この現象を実際のコードで示してみます。TextViewが縦に3つ並んだレイアウトファイルを定義します。

 

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
    <TextView  
        android:id="@+id/txt1"
        android:layout_width="fill_parent" 
        android:layout_height="wrap_content" 
        android:text=""
        />
    <TextView  
        android:id="@+id/txt2"
        android:layout_width="fill_parent" 
        android:layout_height="wrap_content" 
        android:text=""
        />
    <TextView  
        android:id="@+id/txt3"
        android:layout_width="fill_parent" 
        android:layout_height="wrap_content" 
        android:text=""
        />
</LinearLayout>

 

この際、使用する文字列リソースは、次のように定義します。

 

<?xml version="1.0" encoding="utf-8"?>
 <resources>
     <string name="hello_styled"><u>Hello World, TextViewHtmlTest!</u></string>
     <string name="hello_escaped_styled">& lt;u>Hello World, TextViewHtmlTest!& lt;/u></string>
 
     <string name="app_name">TextViewHtmlTest</string>
 </resources>

(実際には&とlt;の間に空白は入れません)

 

hello_styledがタグを含んだそのままの文字列、hello_escaped_styledが<をエスケープ文字に代えて定義した文字列になっています。

アクティビティは、下記のようにします。

 

package com.mori_soft.android.textviewhtmltest;

import android.app.Activity;
import android.os.Bundle;
import android.text.Html;
import android.widget.TextView;

public class TextViewHtmlTest extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        TextView txt1 = (TextView) findViewById(R.id.txt1);
        TextView txt2 = (TextView) findViewById(R.id.txt2);
        TextView txt3 = (TextView) findViewById(R.id.txt3);

        // 文字列リテラルの場合
        {
            String str = "<u>Hello World, TextViewHtmlTest!<u>";
            CharSequence cs = Html.fromHtml(str);
            txt1.setText(cs);
        }

        // stringリソースの場合(失敗例)
        {
            String str = this.getString(R.string.hello_styled);
            CharSequence cs = Html.fromHtml(str);
            txt2.setText(cs);
        }
        
        // stringリソースの場合(成功例)
        {
            String str = this.getString(R.string.hello_escaped_styled);
            CharSequence cs = Html.fromHtml(str);
            txt3.setText(cs);
        }
    }
}

上段のTextViewは、文字列リテラルから生成したテキストを表示します。中段のTextViewは、hello_styledの文字列リソースから取得して生成したテキストを表示します。下段のTextViewは、hello_escaped_styledの文字列リソースを使います。

 

これを動かした結果が、下記のものです。

TextViewでHtmlスタイルの文字列表示のサンプル

上段は、文字列リテラルを処理しているので、アンダーラインが引かれていることがわかります。

しかし、リソースとしてhtmlのタグをそのまま含むhello_styledを使った中央の段はアンダーラインがありません。

一方、リソースとして文字列を定義する際にエスケープ文字を使った下段のものはきちんとアンダーラインが表示されています。

 

背景

Android SDKの開発ガイドのStringのページには、Formating and Stylingの箇所で、String.formatメソッドを使う場合はリソース文字列からhtmlタグが取り除かれる、ということが書かれていますが、getStringの場合には触れていません。

一方、リファレンスのResourcesクラスのgetStringの説明を見ると、きちんとhtmlスタイルのタグが除外されると書かれています。SDKのソースを確認すると、下記のようにContext#getStringは内部でResources#getStringを呼び出しているので、このような振る舞いになることが納得できます。

 

    /**
     * Return a localized string from the application's package's
     * default string table.
     *
     * @param resId Resource id for the string
     */
    public final String getString(int resId) {
        return getResources().getString(resId);
    }

開発ガイドのほうにもgetStringに関する注意を一文入れてほしいものです。