Androidの記事が少なすぎるのでなんとかしてくれ、と言われたので3年振り(!?)に記事を書いてみます。
皆さんレイアウトリソース上のTextViewや汎用ダイアログクラスのメッセージ表示(AlertDialog.Builder.setMessageなど)で文字列の装飾を行いたくなることがあるかと思います。
そんな時に使えるのがHTMLマークアップによるスタイル設定(文字列装飾)です。
基礎的なことは上記公式サイトに説明が載っているのですが、詳細な仕様やまとめ的な情報があまり載っていません。
なので今回はそれらについて記事を書いてみたいと思います。
※1つのTextViewで以下のような表示が可能になります。以下のサンプル画像の表示に使用したテキストは記事末尾に記載してあります。
文字列のスタイル設定についての基本事項
まず基本的な事項として、文字列のスタイル設定について軽く説明しておきます。
Spannable でスタイル設定をするに記載があるように、android.textパッケージ内のクラスを使用してスタイル設定を行うのが基本となります。
ImageSpanを使ってTextView内に画像を表示することも可能です。
2種類の文字列リソース取得方法
文字列に記載の通り、Contextクラスの getStringまたはgetTextメソッドによって文字列リソースの取得が行えます。
サンプルコードなどにあまり登場しないため、案外getTextは使ったことが無い方も多いのでは無いでしょうか。
getTextは説明にある通り、「文字列に適用されたリッチテキストのスタイルを保持」したオブジェクトを取得できます。
スタイルの設定に関しては後述するHTMLタグによって行います。
ソースコードを読むと分かりやすいのですが、getStringはgetText(resId).toString()を呼んでいるだけとなります。
TextViewへ文字列リソースのみでスタイル設定を行う
そして今回の要となるTextViewへの設定の話となります。
みなさんも使用したことがあるかと思いますが、TextViewへソースコード上から文字列を設定する場合はsetText(CharSequence text)メソッドで行います。
このsetTextには引数としてCharSequenceを渡すことができ、ここに前述のContext.getTextで取得したスタイル設定されたCharSequenceを渡すことで、スタイルが反映された文字列がTextViewに表示されます。
また、XMLのレイアウトリソース上での text="@string/xxx"
による文字列指定やTextView.setText(int resid)を使用した場合はContext.getTextで取得した値をTextView.setText(CharSequence text)に設定したのと同じ状態になります。
一応TextView.setText(int resid)のソースコードを確認してみると、内部でResources.getText(Context.getText内部で使われているものと同じ)が使用されています。
Context.getTextによるスタイル設定(レイアウトリソース上のtext="@string/xxx"
指定も含む)
HTML マークアップでスタイル設定をするのページを見ると「以下の HTML 要素をサポートしています。」という記述とともに以下の記載があります。
<b>
テキストを太字にします。<i>
テキストを斜体にします。<u>
テキストにアンダーラインを引きます。
前述のとおり、文字列リソースのIDをそのままレイアウトリソース上などで使用するとContext.getTextで取得したCharSequenceを使用したのと同じ結果となります。
このContext.getTextによるスタイル設定なのですが、実は上記3種類以外のタグも使用することができます。
ただし、残念ながら公式には(おそらく)説明がありません。なのでソースコードを元に説明したいと思います。
細かい点は割愛いたしますが、Context.getTextのソースコードを辿っていくとStringBlockというクラスに辿り着きます。
この中のgetメソッド内でタグの解析などが行われています。ここを見る限り、<b>
<i>
<u>
以外にも以下のタグが処理されているようです。
<tt>
<big>
- 拡大率は1.25倍となっており、重ねがけができます。詳細はここ。
<small>
- 拡大率は0.8倍となっており、重ねがけができます。詳細はここ。
<sup>
<sub>
<strike>
<li>
<marquee>
また、getメソッド内から呼ばれているapplyStyles内では以下のタグが処理を行っていることも見て取れます。
<font>
以下の属性が処理されているようですが、HTMLの仕様とは別物と考えた方が良さそうです。- size
bigやsmallとは違い、絶対値指定でフォントサイズが変更されます。 - height
設定してみたところViewの高さが変わりました。LineHeightSpan.WithDensityを実装したクラスを設定しているようです。 - fgcolor
文字色が変わります。後述の通り、Android 4.3未満では指定しない方が良さそうです。 - bgcolor
背景色が変わります。後述の通り、Android 4.3未満では指定しない方が良さそうです。 - color
文字色が変わります。Android 4.3以降で処理が追加されています。 - face
フォントフェースが変わります。Android 4.3以降で処理が追加されています。
- size
<a>
<annotation>
- このタグの詳細は不明。検索しても全く引っかかりませんでしたがAnnotationクラスがsetSpanされるようです。
一応過去のソースコードと実機確認もしてみましたが、Android 4.3でfontタグのcolorとface属性処理の追加が行われた以外はAndroid 8.1まであまり変更が無いようです。
ちなみに、fontタグのcolorとfgcolor属性の処理内容が4.3以降は全く同じだったため、4.3未満でもfgcolorを指定すれば色指定できるのでは無いかと思って4.2端末で試したところfgcolorとbgcolorの指定はうまく動作しませんでした。(fgcolorを指定すると文字が見えなくなり、bgcolorは変化なし、細かい理由までは確認していません)
別のAndroid 7端末ではfgcolorもbgcolorも意図通りに動きました。
Html.fromHtmlによるスタイル設定
今まで説明してきたContext.getTextの他にもHtml.fromHtmlによるスタイルの設定も可能です。
このHtml.fromHtmlは名前の通りHTML文字列をスタイル付きのテキストに変換するメソッドとなるため、Context.getTextよりも多くのHTMLタグに対応しています。
また、4.3未満でもfontタグのcolor属性に対応しています。ただ、Html.fromHtmlを使用する場合はソースコード上から動的にTextViewへ設定する必要があったり、改行も<br>
や<p>
でやる必要があるなど使い勝手はイマイチかも知れません。
(社内のプロジェクトでは、汎用ダイアログクラスのメッセージ引数をCharSequenceにし、メッセージとしてHtml.fromHtmlの結果を渡すことがたまーにあります)
このメソッドも詳細な仕様はドキュメントに無いと思われるので、ソースコードを見て対応しているタグを確認してみます。
こちらもAndroid4以降のソースコードを複数バージョン確認してみたのですが、Android 7未満とAndroid 7以降で差分があるようです。
以下にAndroid 7未満で処理されているタグを書き出してみます。
<br>
<p>
<div>
も同一の処理結果となる。
<b>
<strong>
も同一の処理結果となる。
<i>
<em>
<cite>
<dfn>
も同一の処理結果となる。
<big>
- 拡大率は1.25倍となっており、重ねがけができます。詳細はここ。
<small>
- 拡大率は0.8倍となっており、重ねがけができます。詳細はここ。
<font>
- 属性はcolorとfaceに対応しています。詳細はここ。
<blockquote>
<tt>
<a>
<u>
<sup>
<sub>
<h1>~<h6>
<img>
- Html.fromHtmlメソッドにHtml.ImageGetterを渡すことで画像の表示が可能です。
以下がAndroid 7で処理が追加されたタグとなります。
<ul>
<li>
<div>
- divはAndroid 7未満にもありますが、7以降はpとは見た目が変わるようです。
<span>
- ここを見る限り、style属性でcolor、background、background-color、text-decoration(line-throughのみ)の指定が有効となるようです。
<del>
<s>
<strike>
も同一の処理結果となる。
aタグについて
TextView.setMovementMethodにLinkMovementMethodを設定することで、タップ時に外部ブラウザを起動する暗黙Intentが発行されます。
タップ時の処理は最終的にURLSpanのonClickが動きます。
タップ時の処理を変更したい場合は、自作のMovementMethodを作成して設定することになります。
fontタグのface属性について
<font face="xxx">
のxxxに入れられる値についてですが、Context.getTextもHtml.fromHtmlも内部でTypefaceSpanを使用しており、以下の3つが指定可能となっています。
- monospace
- serif
- sans-serif
※ただし、フォントが入っていない場合(大抵入っていません・・・)、serifはsans-serifと同じ見た目になる可能性が高い
最後に
以上で本記事におけるHTMLマークアップによるスタイル設定の調査、解説を終わりたいと思います。
一応バージョン間の差分確認や実機での表示確認もある程度はやっていますが、公式の仕様として明記されていない内容が多いため、実際の開発で使う際はその辺りを留意するようお願いいたします。
また、記事の冒頭に掲載したサンプル画像はAndroid 7系の端末で以下のテキストをHtml.fromHtmlによってスタイル設定したものをTextViewに表示したものとなります。
スタイル指定なし<b>太い文字</b><i>イタリック</i>
<br>
<big><big>凄く大きい</big></big><big>大きい</big>普通<small>小さい</small><small><small>凄く小さい</small></small>
<p>
<font color="#ff0000">赤い文字</font>
</p>
<blockquote>ブロック引用</blockquote>
<div>
等幅フォントテストaaaaa11111あああああ<br>
<tt>等幅フォントテストaaaaa11111あああああ</tt>
</div>
<div>
<a href="https://www.google.co.jp/">リンク</a>
<p>
<u>アンダーラインテスト</u>
</p>
<p>
<del>取り消し</del>
</p>
<p>
上付き文字テスト<sup>あいうえお12345abcde</sup>
</p>
<p>
下付き文字テスト<sub>あいうえお12345abcde</sub>
</p>
</div>
<h1>見出し1</h1>
<h6>見出し6</h6>
<ul>
<li>リスト1</li>
<li>リスト2</li>
</ul>
<u><big><font color="#ff0000">大きくて赤い文字でアンダーラインテスト</font></big></u>