Android canvas.drawText中参数x,y分析

772 阅读3分钟

前言

本文将围绕三点展开介绍:

  • 文字如何贴边绘制
  • 文字如何纵向居中对齐
  • 文字如何多行绘制

Canvas 绘制文字

drawText(@NonNull String text, float x, float y, @NonNull Paint paint) 看一下参数:textpaint都很好理解,x , y 乍一看是就是坐标嘛,似乎没那么简单。

分析x,y参数

下面以代码 + 效果截图 + 文字绘制bounds截图三者结合起来分析。

代码

class DrawTextView @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
    private val text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam sed nibh a eros efficitur ultricies. "
    private val bounds = Rect()
    private val paint = TextPaint().also {
        it.isAntiAlias = true
        it.textSize = 200f
        it.textAlign = Paint.Align.LEFT
    }
    override fun onDraw(canvas: Canvas) {
        canvas.drawColor(Color.parseColor("#ffe00ccc"))
        // 获取文字的bounds,然后我们在打印一下,看一下
        paint.getTextBounds(text, 0, text.length, bounds)
        println("rect = $bounds")
        paint.color = Color.LTGRAY
        // 绘制一个背景
        canvas.drawRect(bounds.left.toFloat(), 0f, bounds.right.toFloat(), abs(bounds.bottom).toFloat() + abs(bounds.top).toFloat(), paint)
        paint.color = Color.BLACK
        // 这下绘制文字
        canvas.drawText(text, 0f, -bounds.top.toFloat(), paint)
        // 绘制 -bounds.top.toFloat(),看看y的起始点
        canvas.drawLine(0f, -bounds.top.toFloat()- paint.strokeWidth/2f, width.toFloat(), -bounds.top.toFloat()- paint.strokeWidth/2f, paint)
    }
}

文字 bounds 打印截图

text_bounds.png

从截图中我们可以看到rect的的left = 14, top = -161, right = 9091,bottom = 41,至于为什么top是负数,Android 就是这么设计的,我也不知道为什么。

效果展示截图

drawtext_show.jpg 再分析x, y坐标之前,我们先看看一下我们绘制的灰色的矩形,坐标是left = 14, top = 0, right = 9091, bottom = 202,因为文字是刚好绘制这个矩形区域内的。

分析x坐标

先分析x坐标, 我们在代码中传入的x的值为0f,但是实际效果并不是从0f开始的。从上面的代码上我绘制文字的bounds大小的矩形,矩形的左边刚好和文字的x的坐标点重合。

那么我们就可以得出结论,x并不是文字开始绘制的点,而是包含一点点间距的。如果不想要这个间距怎么办?x = - bounds.left.toFloat()就可以了。

分析y坐标

我们在代码中传入y的值为-bounds.top.toFloat(),也就是161,和绘制的黑色线刚好重合,由此可以得出这就是绘制文字y轴的起始点,即基线baseline,也就是绘制文字的0点,向上为负数,向下为正数。

绘制文字的时候,如何使得文字纵向居中呢? y = 容器的高度/2f - (bounds.top + bounds.bottom)/2f 绘制文字的时候,如何使文字贴顶呢?y = -bounds.top.toFloat()

小结

通过上面的学习,我们知道了两点:

  1. 文字如何纵向居中显示
  2. 文字如何贴边显示

但是以上都是绘制单行文字,那如何绘制多行文字呢?

绘制多行文字

  • 绘制的绘制多行文字,用StaticLayout就可以了,这个API可以自动折行。
  • 也可以使用paint.breakText,这个API给了开发者更大的自由,可以随意定制。

现在我们看看breakText

int breakText(String text, boolean measureForwards, float maxWidth, float[] measuredWidth)

参数介绍:

  • measureForwards: 文字绘制的方向, true从左往右,
  • maxWidth: 最大绘制的宽度
  • measuredWidth: 实际绘制的宽度
  • 返回值 int: 绘制了多少个文字

绘制多行文字的时候,还需要使用paint.getFontSpacing(),这个是行距,就是两行文字baseline的距离。

总结测量文字的几种方法

  • paint.getTextBounds() 获取绘制的字符串的边界,这个和文字有关系
  • paint.getFontMetrics() 获取绘制的字符串的上下边界,比getTextBounds的上下边界大一点,这个和字体有关系
  • paint.measureText() 获取绘制的字符的左右边界,比getTextBounds计算的宽度大一点
  • paint.getTextWidth() 获取单个字的宽度