Android TextView font size & component height adaptive solution

TextView font size does not match component height problem

Usually when making UI, text is an important part.

However, in Android, there are often situations where the actual effect is not consistent with the UI diagram. The main reasons for this situation are the following three points.

Problems

Property values ​​are inconsistent with actual drawing measurements

  • font size
  • font line height
  • Component height

solution

  • Customize the row height attribute and strictly follow the defined value
  • Recalculate the component height through the number of text lines x line height
  • According to the given font size, within the line height range, the font height will not exceed the line height adaptively, and then the font size will be reset.

Specific code implementation

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

}

Reprint: https://www.jianshu.com/p/ccea88e33ebc

Guess you like

Origin blog.csdn.net/gqg_guan/article/details/135213363