Android TextView 字体大小&组件高度自适应解决方

2022-12-21  本文已影响0人  小白cz

TextView字体大小与组件高度不匹配问题

通常在做UI的时候,文本都是比较重要的一部分。

但是在Android中,往往会出现实际效果与UI图不太一致的情况,导致这种情况的出现主要是以下3个点。

问题点

属性值与实际绘制测量结果不一致

效果图

可以看出,无论是单行还是多行,效果都不太理想,字体越大,偏差就越大。

image.png image.png image.png image.png

解决方案

方案效果图

可以看出,无论是单行还是多行,效果都很不错的。

image.png image.png image.png image.png

具体代码实现

package cc.xiaobaicz.playground.widgets

import android.content.Context
import android.graphics.Paint.FontMetrics
import android.os.Build
import android.text.StaticLayout
import android.util.AttributeSet
import android.util.TypedValue
import androidx.appcompat.widget.AppCompatTextView
import cc.xiaobaicz.playground.R
import kotlin.math.max

/**
 * TextView
 * 1. 行高适配
 * @author xiaobai
 */
class Text : AppCompatTextView {

    constructor(context: Context) : this(context, null)

    constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, android.R.attr.textViewStyle)

    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
        isFallbackLineSpacing = false
        val typedArray = context.theme.obtainStyledAttributes(attrs, R.styleable.Text, defStyleAttr, 0)
        lineHeight = typedArray.getDimension(R.styleable.Text_lineHeightX, textSize).toInt()
        typedArray.recycle()
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) {
            // 重新计算高度,适应多行文本
            val layout = StaticLayout.Builder.obtain(text, 0, text.length, paint, measuredWidth).build()
            val height = lineHeightX * layout.lineCount + paddingTop + paddingBottom
            setMeasuredDimension(measuredWidth, height)
        }
    }

    override fun setFallbackLineSpacing(enabled: Boolean) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) return
        super.setFallbackLineSpacing(false)
    }

    var lineHeightX: Int = 0
        private set

    override fun setLineHeight(lineHeight: Int) {
        lineHeightX = max(lineHeight, 8)
        adaptiveTextSize()
        super.setLineHeight(lineHeightX)
        requestLayout()
        invalidate()
    }

    private fun adaptiveTextSize() {
        var metrics = paint.fontMetrics
        while (metrics.fontHeight > lineHeightX) {
            paint.textSize -= max((metrics.fontHeight - lineHeightX) / 2, 1f)
            metrics = paint.fontMetrics
        }
        setTextSize(TypedValue.COMPLEX_UNIT_PX, paint.textSize)
    }

    private val FontMetrics.fontHeight: Float get() = descent - ascent

}
上一篇 下一篇

猜你喜欢

热点阅读