:Tips  TextView を使いこなそう 〜 表示編 〜  その4

様々な SPAN を用い TextView に「より多彩」な表現を 〜前編〜

 「 TextView を使いこなそう 〜 表示編 〜」の その3 、に引き続き TextView の4回目です。



SPAN を用いる意義・・・

  Html#fromHtml() を用いることで、文字の大きさ、色、スタイルの変更。アンダーラインを引く。画像を文字列中に挿入する。等々が可能になりました。大凡の表現にはこれで十分でしょう。しかしもう一歩、踏み込んだ表現をしたいといった場合にはこれだけでは行き詰まる事になります。例えば、文字サイズを大きくする、小さくする、といっても倍率は固定です。大きさをもっと自由に設定したい場合はどうするのでしょう。他にも、ある行だけを「右揃え」にしたい、「中央揃え」にしたいといった要求もあるでしょう。

Spannable インターフェイスの setSpan() メソッド引数に様々な用途の SPAN を渡す



Html#fromHtml() で実際に行われていること・・・

 実際に Html#fromHtml() メソッド内で行われている処理というのは、 Html#fromHtml() に引数として渡された文字列にHTMLタグが見つかると、その都度 Spannable インターフェイス実装済みのオブジェクト SpannableStringBuilder のメソッドである setSpan() へ引数として「適合する用途の SPAN 」インスタンスを生成し渡すといった作業を繰り返しています。逆に言えば、HTMLタグと同様の機能を持つものが SPAN であるともいえます。これと同様の処理を自ら記述することにより Html#fromHtml() よりも、さらにきめ細かい、多彩な表現が実現できることになります。



Spannable インターフェイスとは・・・

Spannable インターフェイスは SPAN を組み込む機能を提供します。このインターフェイスを実装したクラスであれば SPAN を組み込むことが可能です。組み込み時に使われるメソッドが setSpan() です。

android.text.Spannable#

public abstract void setSpan (Object what, int start, int end, int flags) 

第1引数の what が SPAN のインスタンス、第2引数 start が開始位置、第3引数 end が終了位置、第4引数 flags がこの SPAN の影響範囲指定となります。第2引数がHTMLタグの <> 、第3引数がHTMLタグの </> の位置と考えれば分かり易いかもしれません。問題は第4引数の flags についなのですが、ここは定数での指定になります。定数は android.text.Spanned にある定数を使用します(ちなみに Spannable は Spanned がインプリメントされていますので Spannable の定数としても構いません)。 Spanned には多くの定数が存在しますが重要なのは1つです(といいますか、他の使い方・使い道が正直あまり分かりません汗)

android.text.Spanned#

public static final int SPAN_EXCLUSIVE_EXCLUSIVE 

これを第4引数に指定することで「開始位置」から「終了位置」までの範囲が第1引数で渡された SPAN の影響範囲となります。



Spannable インターフェイスの実装クラスは・・・

Spannable インターフェイスを実装しているクラスは以下の2つです。

SpannableString クラスと SpannableStringBuilder クラスの違いは、文字列の追加が可能かどうかの違いです。文字列固定ならば前者、文字列の追加が必要な場合は後者を使ってください。



主な SPAN ・・・

SPAN は用途に合わせて様々なクラスが用意されています。詳細はリファレンスの android.text.style を参照してください。以下に主だった幾つかの SPAN を例示します。

SPAN 用途
android.text.style.AbsoluteSizeSpan 文字サイズを絶対値で設定します。
android.text.style.RelativeSizeSpan 文字サイズを相対値で設定します。
android.text.style.AlignmentSpan.Standard 文字列の行の水平位置(右揃え、又は中央揃え)を設定します。
android.text.style.BackgroundColorSpan 背景色を設定します。
android.text.style.MaskFilterSpan 文字にブラー(ぼかし)効果やエンボス(浮き出し)効果等を設定します。
android.text.style.ScaleXSpan 横に文字を伸縮させる設定をします。



AbsoluteSizeSpan

AbsoluteSizeSpan は文字のサイズを絶対値(ピクセル)で変化させる際に用います。

次に、AbsoluteSizeSpan のコンストラクタを示します。

public AbsoluteSizeSpan (int size) 

コンストラクタの引数 size には表示したい大きさの値(ピクセル)を渡します。例えば 100px としたい場合は・・・

AbsoluteSizeSpan span = new AbsoluteSizeSpan(100);

としてインスタンスを生成します。

生成したインスタンスを setSpan() メソッドの引数として渡します。

SpannableString spannable = new SpannableString("ABCDE");
spannable.setSpan(span, 2, 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

上記の例ですと「 CD 」の文字列が 100px の文字サイズで表示されることになります。

サンプルコードです。

public class TextViewTest extends Activity {
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        TextView tv = new TextView(this);
        
        // 文字列 "ABCDE" を引数に渡し SpannableString のインスタンスを生成。
        SpannableString spannable = new SpannableString("ABCDE");
        
        // 100px の文字サイズの SPAN インスタンスを生成。
        AbsoluteSizeSpan span = new AbsoluteSizeSpan(100);
        
        // SPAN を SpannableString に組み込む。
        spannable.setSpan(span, 2, 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        
        tv.setText(spannable);
        setContentView(tv, new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
    }
}

結果はこうなります・・・


以上です。





RelativeSizeSpan

RelativeSizeSpan は文字のサイズを相対値(倍率)で変化させる際に用います。

次に RelativeSizeSpan のコンストラクタを示します。

public RelativeSizeSpan (float proportion) 

コンストラクタの引数 proportion にはデフォルトの文字サイズから変更したい倍率の値を渡します。例えば 2.5 倍にしたい場合は・・・

RelativeSizeSpan span = new RelativeSizeSpan(2.5f);

としてインスタンスを生成します。

生成したインスタンスを setSpan() メソッドの引数として渡します。

SpannableString spannable = new SpannableString("ABCDE");
spannable.setSpan(span, 2, 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

上記の例ですと「 CD 」の文字列が 2.5 倍の文字サイズで表示されることになります。

サンプルコードです。

public class TextViewTest extends Activity {
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        TextView tv = new TextView(this);
        
        // 文字列 "ABCDE" を引数に渡し SpannableString のインスタンスを生成。
        SpannableString spannable = new SpannableString("ABCDE");
        
        // 2.5 倍の文字サイズの SPAN インスタンスを生成。
        RelativeSizeSpan span = new RelativeSizeSpan(2.5f);
        
        // SPAN を SpannableString に組み込む。
        spannable.setSpan(span, 2, 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        
        tv.setText(spannable);
        setContentView(tv, new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
    }
}

結果はこうなります・・・


以上です。





AlignmentSpan.Standard

AlignmentSpan.Standard は文字列を右寄せ(右揃え)、中央寄せ(中央揃え)、左寄せ(左揃え)といったようにテキストの水平位置を変化させる際に用います。

次に AlignmentSpan.Standard のコンストラクタを示します。

public AlignmentSpan.Standard (Layout.Alignment align) 

コンストラクタの引数 align には水平位置を表す android.text.Layout.Alignment の定数を渡します。

android.text.Layout.Alignment の定数は以下の3つです。

public static final Layout.Alignment ALIGN_CENTER
public static final Layout.Alignment ALIGN_NORMAL 
public static final Layout.Alignment ALIGN_OPPOSITE

注・定数の命名から推察すると ALIGN_CENTER は中央、 ALIGN_NORMAL がデフォルト、ALIGN_OPPOSITE がデフォルトの反対側という感じかと思ったのですが、そうはなっていないようです。 TextView#setGravity() 等でデフォルトの設定を「右揃え」にしても、関係なく結果は ALIGN_NORMAL は左揃え、 ALIGN_OPPOSITE は右揃えになるようです。デフォルトが「左揃え」であれば問題はありませんが、もし「右揃え」にしているケースでは後々修正が入った場合に問題が起こる可能性があるので注意が必要です。



サンプルコードです。

public class TextViewTest extends Activity {
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        TextView tv = new TextView(this);
        
        // SpannableStringBuilder のインスタンスを生成。
        SpannableStringBuilder spannable = new SpannableStringBuilder();
        
        // テキストの追加。
        spannable.append("左揃え");
        spannable.append("\n");      // 改行
        
        int start = spannable.length();
        
        // テキストの追加。
        spannable.append("中央揃え");
        spannable.append("\n");     // 改行
        
        int end = spannable.length();
        
        // 中央揃えの SPAN インスタンスを生成。
        AlignmentSpan.Standard center_span = new AlignmentSpan.Standard(Layout.Alignment.ALIGN_CENTER);
        
        // SPAN を SpannableStringBuilder に組み込む。
        spannable.setSpan(center_span, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        
        start = spannable.length();
        
        // テキストの追加。
        spannable.append("右揃え");
        spannable.append("\n");     // 改行
        
        end = spannable.length();
        
        // 右揃えの SPAN インスタンスを生成。
        AlignmentSpan.Standard right_span = new AlignmentSpan.Standard(Layout.Alignment.ALIGN_OPPOSITE);
        
        // SPAN を SpannableStringBuilder に組み込む。
        spannable.setSpan(right_span, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        
        tv.setText(spannable);
        setContentView(tv, new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
    }
}

ごちゃごちゃして見にくい(醜い)コードですが、同一の TextView に通常→中央寄せ→右寄せ、と3つのケースを表示するために似通った処理を3回繰り返しているだけです。 start と end は setSpan() の第2引数と第3引数に用いる為にテキスト追加前の文字数と追加後の文字数を保持しておく為の変数です。今回は、テキストを複数回追加するので SpannableStringBuilder を用います。最初に SpannableStringBuilder のインスタンスを作ります。そして新たなテキストを追加していきます。新たなテキストを追加する場合に用いるメソッドは append() になります。このメソッドは文字列が連結されると共に、以前に組み込まれた SPAN も同様に連結されて保持されていきます。次に AlignmentSpan のインスタンスを作ります。引数は中央揃えなら Layout.Alignment.ALIGN_CENTER 。右揃えならば Layout.Alignment.ALIGN_OPPOSITE 。今回は使いませんでしたが、左揃えならば Layout.Alignment ALIGN_NORMAL を渡します。最後に setSpan() に各 SPAN インスタンスを渡し終了です。



結果はこうなります・・・


以上です。




次回は・・・

次回も引き続き、残った「 BackgroundColorSpan 」「 MaskFilterSpan 」「 ScaleXSpan 」を書く予定です。