Help us understand the problem. What is going on with this article?

AndroidのTextViewの高さを考慮してフォントサイズを自動調整する

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

スクリーンショット 2019-05-19 22.57.38.png

環境:Android Studio 3.3.1、Kotlin 1.3

AndroidでもAndroid 8.0(API Level 26)からAutoSizing TextViewsが追加されました。
しかしレイアウトの都合でTextViewのwidthを"wrap_content"にしなければならない場合はこのAutoSizing TextViewsでもフォントが自動調整されません。
そこで以下のサイトなどを参考に自分でフォントサイズを調整することが多いかと思うのですが、

【Android】横幅に合わせてテキストサイズを調整する

この方法の場合、TextViewが複数行表示できる高さを持っている時にも、テキストを1行で表示する大きさまでフォントサイズを小さくしてしまいます。
TextViewがテキストを複数行表示できる高さを持っている場合は、テキストを複数行にしてぎりぎり表示できるフォントサイズに留めておきたいですよね。
そこで上記のサイトのコードに少し手を加えて以下の様にしてみました。

コード

AutoFontTextView.kt
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で公開しています。

https://github.com/naosekig/AutoFontFitTextView

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした