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