今回はAndroidのTextViewのフォントサイズの自動調整に関するお話です。

環境:Android Studio 3.3.1、Kotlin 1.3
AndroidでもAndroid 8.0(API Level 26)からAutoSizing TextViewsが追加されました。
しかしレイアウトの都合でTextViewのwidthを"wrap_content"にしなければならない場合はこのAutoSizing TextViewsでもフォントが自動調整されません。
そこで以下のサイトなどを参考に自分でフォントサイズを調整することが多いかと思うのですが、
この方法の場合、TextViewが複数行表示できる高さを持っている時にも、テキストを1行で表示する大きさまでフォントサイズを小さくしてしまいます。
TextViewがテキストを複数行表示できる高さを持っている場合は、テキストを複数行にしてぎりぎり表示できるフォントサイズに留めておきたいですよね。
そこで上記のサイトのコードに少し手を加えて以下の様にしてみました。
コード
import android.content.Context
import android.graphics.Paint
import android.util.AttributeSet
import android.util.TypedValue
import android.widget.TextView
class AutoFontFitTextView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : TextView(context, attrs, defStyleAttr) {
override fun onLayout(changed: Boolean, left:Int, top: Int, right: Int, bottom: Int) {
super.onLayout(changed, left, top, right, bottom)
resize()
}
private fun resize() {
val viewHeight = this.height
val viewWidth = this.width
var textSize = this.textSize
var textHeight = calcTextHeight(textSize,viewWidth)
while (viewHeight <= textHeight) {
if (1f >= textSize) {
textSize = 1f
break
}
textSize -= 1f
textHeight = calcTextHeight(textSize,viewWidth)
}
setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize)
}
private fun calcTextHeight(textSize:Float,viewWidth:Int): Float {
val paint = Paint()
paint.textSize = textSize
val textHeightPerRow = paint.getFontMetrics(null)
val textPerLines = this.text.toString().split("\n")
var rowCount = 0
for (i in 0..textPerLines.size - 1) {
val textWidth = paint.measureText(textPerLines[i])
rowCount += Math.ceil(textWidth.toDouble() / viewWidth.toDouble()).toInt()
if (i != textPerLines.size - 1) {
rowCount += 1
}
}
return textHeightPerRow * rowCount
}
}
解説
Paintを利用してテキストのサイズを測るところは同じです。
テキストサイズをPaintに設定した後、以下のコードでテキストの1行あたりの高さが取れます。
val textHeightPerRow = paint.getFontMetrics(null)
次にテキストを表示するために必要な行数を割り出します。
今回はテキストに改行文字が入っているケースも想定しています。
val textPerLines = this.text.toString().split("\n")
var rowCount = 0
for (i in 0..textPerLines.size - 1) {
val textWidth = paint.measureText(textPerLines[i])
rowCount += Math.ceil(textWidth.toDouble() / viewWidth.toDouble()).toInt()
if (i != textPerLines.size - 1) {
rowCount += 1
}
}
「1行あたりの高さ * 必要な行数」でテキストの高さが得られますので、それがTextViewの高さ以内に収まるまでフォントサイズを下げます。
GitHub
今回のサンプルコードは以下のGitHubで公開しています。