6.2.1 概述
1. 四线格与基线
小时候,我们在刚开始学习写字母时,用的本子是四线格的,我们必须把字母按照规则写在四线格内。
在 canvas 在利用 drawText 绘制文字时,也是有规则的,这个规则就是基线!我们先来看一下什么是基线:
可见基线就是四线格中的第三条线。也就是说,只要基线的位置定了,那文字的位置必然是定了的!
2. canvas.drawText()
1)canvas.drawText() 与基线
上面这个构造函数是最常用的 drawText 方法,传进去一个 String 对象就能画出对应的文字。但这里有两个参数需要非常注意,表示原点坐标的 x 和 y。很多同学可能会认为,这里传进去的原点参数 (x,y) 是所在绘制文字所在矩形的左上角的点。但实际上并不是!比如,我们上面如果要画 “harvic’s blog” 这几个字,这个原点坐标应当是下图中绿色小点的位置。
一般而言,(x,y) 所代表的位置是所画图形对应的矩形的左上角点。但在 drawText 中是非常例外的,y 所代表的是基线的位置。
2)示例
首先,我们把 (0,200) 所在的这条横线画出来,所以我先画了一条线从点坐标为 (0,200) 到点坐标为 (3000,200) 的一条直线,然后利用 canvas.drawText 以 (0,200) 为原点画出文字,最终效果图如下:
结论:
- drawText() 函数中的参数 y 是基线的位置。
- 一定要清楚的是,只要 x 坐标、基线位置、文字大小确定,文字的位置就是确定了。
3. paint.setTextAlign() 函数
在上面我们讲了,drawText() 函数中的 y 参数表示所要绘制文字的基线所在位置。从上面的例子中可以看到,绘制是从 x 坐标的右边开始的,但这并不是必然的结果。我们来看一张图:
在 drawText(text, x, y, paint) 中传进去的原点坐标 (x,y)。其中,y 表示的基线的位置。那 x 代表什么呢?从上面的例子运行结果来看,应当是文字开始绘制的地方。
并不是!x 代表所要绘制文字所在矩形的相对位置。相对位置就是指指定点 (x,y) 在在所要绘制矩形的位置。我们知道所绘制矩形的纵坐标是由 y 值来确定的,而相对 x 坐标的位置,只有左、中、右三个位置了。也就是所绘制矩形可能是在 x 坐标的左侧绘制,也有可能在 x 坐标的中间,也有可能在 x 坐标的右侧。而定义在 x 坐标在所绘制矩形相对位置的函数是:
仍然使用上面的例子,当设置不同的 Align 取值时,效果如下图所示。
Paint.Align.LEFT:
Paint.Align.CENTER:
Paint.Align.RIGHT:
4. 注意
这里需要再次强调的是:相对位置是根据所要绘制文字所在矩形来计算的。比如,只写一个大写字母 A,将其相对位置设置为 Paint.Align.CENTER。
效果如下图所示:
6.2.2 绘图四线格与 FontMetrics
1. 文字的绘图四线格
除了基线以外,系统在绘制文字时还有 4 条线,分别是 ascent、descent、top、bottom,如下图所示。
- ascent:系统建议的,绘制单个字符时,字符应当的最高高度所在线。
- descent:系统建议的,绘制单个字符时,字符应当的最低高度所在线。
- top:可绘制的最高高度所在线。
- bottom:可绘制的最低高度所在线。
我们在绘制文字时,ascent 是推荐的绘制文字的最高高度,就表示在绘制文字时,尽力要在这个最高高度以下绘制文字。descent 是推荐的绘制文字的最底高度线,同样表示是在绘制文字时尽量在这个 descent 线以上来绘制文字。而 top 线则指该文字可以绘制的最高高度线,bottom 则是表示该文字可以绘制的最低高度线。ascent、descent 是系统建议上的绘制高度,而 top、bottom 则是物理上屏幕最高、最低可以画的高度值。
2. FontMetrics
1)FontMetrics 概述
我们知道基线的位置是我们在构造 drawText() 函数时由参数 y 来决定的,那 ascent、descent、top、bottom 这些线的位置要怎么计算出来呢?
Android 给我们提供了一个类:FontMetrics,它里面有四个成员变量:
他们的意义与值的计算方法分别如下:
- ascent = ascent 线的 y 坐标 - baseline 线的 y 坐标。
- descent = descent 线的 y 坐标 - baseline 线的 y 坐标。
- top = top 线的 y 坐标 - baseline 线的 y 坐标。
- bottom = bottom 线的 y 坐标 - baseline 线的 y 坐标。
我们再来看个图:
从这个图中,我们先说明两点,然后再回过头来看上面的公式:
1、X 轴,Y 轴的正方向走向是 X 轴向右是正方向,Y 轴向下是正方向,所以越往下 Y 坐标越大!
2、大家千万不要将 FontMetrics 中的 ascent、descent、top、bottom 与现实中的 ascent、descent、top、bottom 所在线混淆!这几条线是真实存在的,而 FontMetrics 中的 ascent、descent、top、bottom 这个变量的值就是用来计算这几条线的位置的。
FontMetrics 的这几个变量的值都是以 baseline 为基准的,对于 ascent 来说,baseline 线在 ascent 线之下,所以必然 baseline 的 y 值要大于 ascent 线的 y 值,所以 ascent 变量的值是负的。
同理,对于 descent 而言:
descent 线在 baseline 线之下,所以必然 descent 线的 y 坐标要大于 baseline 线的 y 坐标,所以 descent 变量的值必然是正数。
2)得到 Text 四线格的各线位置
先列出一个公式:
推算过程如下:
因为 ascent 线的 Y 坐标等于 baseline 线的 Y 坐标减去从 baseline 线到 ascent 线的这段距离。也就是:(|fontMetric.ascent| 表示取绝对值)。
ascent 线 Y 坐标 = baseline 线 Y 坐标 - |fontMetric.ascent|;
又因为 fontMetric.ascent 是负值,所以:
ascent 线 Y 坐标 = baseline 线 Y 坐标 - |fontMetric.ascent|;
ascent 线 Y 坐标 = baseline 线 Y 坐标 - (-fontMetric.ascent);
ascent 线 Y 坐标 = baseline 线 Y 坐标 + fontMetric.ascent;
这就是整个推算过程,没什么难度,同理可以得到:
- ascent 线 Y 坐标 = baseline 线的 y 坐标 + fontMetric.ascent;
- descent 线 Y 坐标 = baseline 线的 y 坐标 + fontMetric.descent;
- top 线 Y 坐标 = baseline 线的 y 坐标 + fontMetric.top;
- bottom 线 Y 坐标 = baseline 线的 y 坐标 + fontMetric.bottom;
3)获取 FontMetrics 对象
从这里可以看到,通过 paint.getFontMetrics() 得到对应的 FontMetrics 对象。这里还有另外一个 FontMetrics 同样的类叫做 FontMetricsInt,它的意义与 FontMetrics 完全相同,只是得到的值的类型不一样而已,FontMetricsInt 中的四个成员变量的值都是 Int 类型,而 FontMetrics 得到的四个成员变量的值则都是 float 类型的。
4)示例:计算 Text 四线格位置
|
|
6.2.3 常用函数
1. 字符串所占高度和宽度
1)高度
字符串所占高度很容易得到,直接用 bottom 线所在位置的 Y 坐标减去 top 线所在位置的 Y 坐标就是字符串所占的高度:
2)、宽度
宽度是非常容易得到的,直接利用下面的函数就可以得到:
2. 最小矩形
1)概述
要获取最小矩形,也是通过系统函数来获取的,函数及意义如下:
示例:
可以看到这个矩形的左上角位置为(8,-90),右下角的位置为(654,25);大家可能会有疑问,为什么左上角的 Y 坐标是个负数?从代码中,我们也可以看到,我们并没有给 getTextBounds() 传递基线位置。那它就是以(0,0)为基线来得到这个最小矩形的,所以这个最小矩形的位置就是以(0,0)为基线的结果。
2)得到最小矩形的实际位置
我们先来看一个原理图:
在上面这个图中,我们将黑色矩形平行下移距离Y(黄色线依照的是基线的位置),那么平移后的左上角点的 y 坐标就是 y2 = y1 + Y。
同样的道理,由于 paint.getTextBounds() 得到最小矩形的基线是 y = 0;那我们直接将这个矩形移动 baseline 的距离就可以得到这个矩形实际应当在的位置了。
所以矩形应当所在实际位置的坐标是:
3)完整的代码
6.2.4 示例:定点写字
1. 给定左上顶点绘图
在这个图中,我们给定左上角的位置,即 (left,top);我们知道要画文字,drawText() 中传进去的 Y 坐标是基线的位置,所以我们就必须根据 top 的位置计算出 baseline 的位置。
我们来看一个公式:
FontMetrics.top = top - baseline;
所以:
baseline = top - FontMetrics.top;
因为 FontMetrics.top 是可以得到的,又因为我们的 top 坐标是给定的,所以通过这个公式就能得到 baseline 的位置了。
代码:
2. 给定中间线位置绘图
先来看一张图:
在这个图中,总共有四条线:top 线、bottom 线、baseline 和 center线。其中 center 线正是在 top 线和 bottom 线的正中间。
为了方便推导公式,另外标了三个距离 A、B、C。显然,距离 A 和距离 C 是相等的,都等于文字所在矩形高度以的一半,即 A = C = (bottom - top)/2。
又因为:
bottom = baseline + FontMetrics.bottom
top = baseline + FontMetrics.top
将这两个公式代入上面的公式,就可得到:
A = C = (FontMetrics.bottom - FontMetrics.top)/2
而距离 B 则表示 center 线到 baseline 的距离。很显然距离
B = C - (bottom - baseline)
又因为:
FontMetrics.bottom = bottom - baseline
C = A
所以:
B = A - FontMetrics.bottom
从而有:
baseline = center + B = center + A - FontMetrics.bottom = center + (FontMetrics.bottom - FontMetrics.top)/2 - FontMetrics.bottom
根据上面的推导过程,我们最终可知,当给定中间线 center 位置以后,baseline 的位置为:
baseline = center + (FontMetrics.bottom - FontMetrics.top)/2 - FontMetrics.bottom