In UI development, the label text label is the most commonly used. Android provides TextView to help us display text. The Android Canvas also provides drawText to help us draw the display text when customizing the view.

Take a quick look at this code

private val paint = Paint(Paint.ANTI_ALIAS_FLAG)

init {
    paint.textSize = 150F
    paint.style = Paint.Style.FILL_AND_STROKE
    paint.color = Color.parseColor("#FFFFFF")}override fun onDraw(canvas: Canvas?). {
    super.onDraw(canvas) canvas? .drawColor(Color.parseColor("#80F14400"))
    val textLabel = "Hello World, Hello Python"canvas? .drawText(textLabel,0F.0F, paint)
}
Copy the code

The above code is the simplestdrawTextUse. According to our understanding of the Android coordinate system, it seems that there is no problem. It should draw the text in the upper left corner of the view, and the text is fully displayed.From the real display, the effect is completely inconsistent with what we want. Why does this happen? So let’s seedrawTextMethod definition.You can see this in the method commentsdrawTextthexandyThese are used to define the starting point of text drawing, and this starting pointyThe coordinates are based onbaseline(Baseline). Want to understandbaselineTo understand the measurement rules of words,canvasText measurement rules are usedFontMetricsMethod defined in. whileFontMetricsfromPaint.getFontMetrics()To obtain.

Before looking at the definition of FontMetrics, take a look at this graph, which was copied from someone else’s blog.

Leading refers to the extra spacing of rows, i.e. the distance between the ascending bottom line and the descending top line for the two adjacent rows

The FontMetics class mainly defines the measurement properties of the text.

public static class FontMetrics {
    /** * The maximum distance above The baseline for The tallest glyph in * The font at a given text size. The maximum distance above the baseline of the highest font in a font. * /
    public float   top;
    /** * The recommended distance above The baseline for single-line delimited text. * /
    public float   ascent;
    /** * The recommended distance below The baseline for Singled out text. * Recommended distance below The baseline for singled out text. * /
    public float   descent;
    /** * The maximum distance below The baseline for The lowest glyph in * The font at a given text size. The maximum distance below the baseline of the lowest font below the font. * /
    public float   bottom;
    /** * The recommended additional space to add between lines of text. * Add additional Spaces between lines of text. * /
    public float   leading;
}
Copy the code

As you can see from the FontMetrics definition above and from the drawText, baseline is used as a baseline to determine the distance.

You can see thatbaselineSo let’s seebaselineHow do you calculate it?As you can see from the picture,baselineThe size of theyValue is equal to thefontMetrics.bottom + fontMetrics.top -fontMetrics.descent . Well, you can print it outFontMetricsTo see the output.

val fontMetrics = paint.fontMetrics
Log.e("info"."fontMetrics-->top: " + fontMetrics.top)
Log.e("info"."fontMetrics-->leading: " + fontMetrics.leading)
Log.e("info"."fontMetrics-->ascent: " + fontMetrics.ascent)
Log.e("info"."fontMetrics-->descent: " + fontMetrics.descent)
Log.e("info"."fontMetrics-->bottom: " + fontMetrics.bottom)
Copy the code

E/info: fontMetrics–>top: -157.2 E/info: fontMetrics–>leading: 0.0e /info: fontMetrics–>ascent: -139.2 E/info: Descent: fontMetrics–> Descent: 36.600002 E/info: fontMetrics–> Bottom: 40.65

Comparing the printed results with the graph, it will be found that the starting data is based on the baseline, the data above the baseline is negative and the data below the baseline is positive.

Now that we know what each value means and how the baseline is calculated, we can use the baseline as a guide line and text to draw each attribute, right

val fontMetrics = paint.fontMetrics
val baseline = fontMetrics.bottom + kotlin.math.abs(fontMetrics.top) - fontMetrics.descent
canvas?.save()
// move zero y to baselinecanvas? .translate(0F,
    baseline
)
paint.color = Color.BLUE
val textLabel = "Hello,Python"canvas? .drawText(textLabel,0F.0F, paint)
paint.style = Paint.Style.STROKE
/ / draw the toppaint.color = Color.BLUE canvas? .drawLine(0F, fontMetrics.top, width.toFloat(), fontMetrics.top, paint)
paint.color = Color.YELLOW
/ / draw the ascentcanvas? .drawLine(0F, fontMetrics.ascent, width.toFloat(), fontMetrics.ascent, paint)
paint.color = Color.GRAY
/ / draw the descentcanvas? .drawLine(0F, fontMetrics.descent, width.toFloat(), fontMetrics.descent, paint)
paint.color = Color.parseColor("#F15500")
/ / draw the bottomcanvas? .drawLine(0F, fontMetrics.bottom, width.toFloat(), fontMetrics.bottom, paint)
/ / draw the baseline
paint.color = Color.parseColor("#8080FF")
paint.pathEffect = DashPathEffect(floatArrayOf(10F.10F), 0F) canvas? .drawLine(0F.0F, width.toFloat(), 0F, paint) canvas? .restore()Copy the code

If you want to understand the system, welcome to subscribe to my TextView special lecture, so that you can thoroughly understand the principle of TextView drawing, from the perspective of the source code to understand Google design ideas.

Welcome to the Textview – to show you how to draw a Textview