TextView字体巨细与组件高度不匹配问题
通常在做UI的时候,文本都是比较重要的一部分。
但是在Android中,往往会出现实际效果与UI图不太一致的状况,导致这种状况的出现首要是以下3个点。
问题点
属性值与实际绘制测量结果不一致
- 字体巨细
- 字体行高
- 组件高度
效果图
能够看出,无论是单行还是多行,效果都不太理想,字体越大,误差就越大。
解决计划
- 自界说行高属性,严厉遵照界说数值
- 经过文本行数x行高,从头核算组件高度
- 依据给定字号,在行高范围内,自习惯调整字体高度不会超越行高,再从头设置字号巨细
计划效果图
能够看出,无论是单行还是多行,效果都很不错的。
详细代码实现
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
}