やりたいこと
高さがwrap_contentなTextViewに動的にテキストをセットして、長すぎて全文表示できない時にandroid:ellipsizeの設定を反映して…にしてほしい。
※maxLinesは機種ごと、端末の表示文字サイズ設定ごとに異なり、なるべく無駄なくたくさんテキストを表示したいので設定できないというとき。
cf ) 何もしないと
以下のようなTextViewに何も考えず長いテキストをセットすると、…は表示されず表示できるだけ表示して、UI上では入りきらなかった部分はなかったかのようになってしまう。場合によっては最後の行は文字が下の方見切れたりする。
<TextView
android:id="@+id/text1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:textSize="10sp" />
解決法
テキストをセットした後に、今の環境での見切れず表示できるmaxLineを計算してやってTextViewにセットする。
val text1 = findViewById<TextView>(R.id.text1)
if (text1.layout.height > text1.measuredHeight) {
// textが見切れるようだったら、maxlineを設定して長すぎる部分が...になるようにする。
// text.layout.height : 設定されてる文字がかかれてる高さ。ここにかかれている文字が実際の画面上ですべて表示されているわけではない。
// text1.measuredHeight : 実際に画面上で表示されている高さ。
// text1.measuredHeightを設定しても、maxLineを設定しない限りbody.layout.heightまでは変わらないらしい。
// text1.lineHeight : 一行の高さ。
// text1.paint.fontSpacing : 行間の大きさ。
text1.maxLines = text1.measuredHeight / (text1.lineHeight + text1paint.fontSpacing.roundToInt())
}
解説
コードにも書いてあるが、各プロパティは
- text1.layout.height : 設定されてる文字がかかれてる高さ。ここにかかれている文字が実際の画面上ですべて表示されているわけではない。
- text1.measuredHeight : 実際に画面上で表示されている高さ。
- text1.lineHeight : 一行の高さ。
- text1.paint.fontSpacing : 行間の大きさ。
で取得できる。
ここで注意したいのは、TextView#measureで変えたり、TextView#getMeasuredHeightで取得出来たりする大きさはテキストがかかれている部分の大きさとイコールではないということ。text1.layout.heightのうち、text1.measuredHeightで表示できる部分のみ切り取られてUI上に描画されるイメージである。なので長いテキストをセットしてしまうとtext1.layout.heightだけ長くなるので途中で(下手すると最後の行は下が見切れた状態で)表示が切れてしまう。
しかし、maxLinesを設定すると、text1.layout.heightもそれに合わせて大きさが変わり入りきらない部分があったときはandroid:ellipsizeの設定が反映される。
なので、長さがwrap_contentなTextViewに動的にテキストをセットして、長すぎて全文表示できない時にandroid:ellipsizeの設定を反映させるには、text1.layout.height > text1.measuredHeightか調べてtrueの時はtext1.measuredHeightを一行の高さと行間の大きさの和で割ってやって動的にmaxLinesを出してやってセットしてやればいい。
注意点
layoutファイルでTextViewにandroid:ellipsizeを何も設定してやらないと、maxLinesを設定してやった後にテキストが何も表示されなくなった。なのでlayoutファイルでのandroid:ellipsizeの設定は必須