1、文字的绘制
1.1、四线格与基线
先看下文字在四线格中的展示
![](https://img.haomeiwen.com/i7264052/40975ad1404fc632.png)
在Canvas中绘制文字也是有规则的,这个规则就是基线,下面看下什么是基线
![](https://img.haomeiwen.com/i7264052/e535a9813eb5d875.png)
从上图可见,只要基线确定了,那么文字的位置就确定。
1.2、drawText()与基线的关系
public void drawText(@NonNull String text, float x, float y, @NonNull Paint paint)
上面函数是常用的绘制文字的方法,其中(x,y)并不是绘制文字的左上角点,而是基线开始处的位置,如下图小点的位置就是点(x,y)
![](https://img.haomeiwen.com/i7264052/91f76d0d00c0957a.png)
可见:
- drawText()函数中y就是基线的位置
- 只要x坐标、基线的位置、文字的大小确定了,文字的位置就确定了。
1.3、绘图四线格和FontMetrics
1.3.1、文字的绘图四线格
除了基线之前,绘制文字还有4条线:ascent、descent、top和bottom,如下图所示
![](https://img.haomeiwen.com/i7264052/d79e2018580fb876.png)
- ascent:绘制单个字符时,字符应当的最高高度所在线
- descent:在绘制单个字符时,字符应当的最低高度所在线
- top:可绘制的最高高度所在线
-
bottom:可绘制的最低高度所在线
单看字面意思,可能还是不好理解它们的区别,下面举个例子:
image.png
图中灰色部分表示电视屏幕,白色虚线框表示安全区域框,这个安全区域框就是系统推荐的显示区域,但是由于制式不同,每个国家电视屏幕大小不一致,可能会导致视频被裁剪,但是系统推荐的区域内,无论那种制式都是可以完整显示的。
同样,在绘制文字时,ascent是系统推荐的绘制文字最高高度所在,表示在绘制文字时,要尽量在这个最高高度线以下进行绘制,descent是系统推荐的最低高度所在线,要尽量在这个最低高度线以上进行绘制。top表示文字可绘制的最高高度线所在,bottom表示文字可绘制的最低高度线所在。ascent、descent是系统推荐的绘制高度,而top、bottom是物理上最高、最低可绘制的高度。
1.3.2、FontMetrics
上面所提到的ascent、descent、top、bottom是如何计算的呢?系统提供了FontMetrics,它里面有4个成员变量:
public float ascent;
public float bottom;
public float descent;
public float leading;
public float top;
它们的计算方式和含义如下:
- ascent=ascent线的y坐标-基线的y点坐标
- descent=descent线的y坐标-基线的y点坐标
- top=top点的y坐标-基线的y点坐标
- bottom=bottom点的y坐标-基线的y点坐标
示例:绘制出各条线的位置
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
paint.setColor(Color.RED)
paint.textSize=120f
canvas?.drawText("harvic \\’ s blog", baselinex, baselineY, paint)
val fontMetrics = paint.getFontMetrics()
val ascentY = fontMetrics.ascent + baselineY
val dsecentY = fontMetrics.descent + baselineY
val topY = fontMetrics.top + baselineY
val bottomY = fontMetrics.bottom + baselineY
val mWidth = width.toFloat()
val lineArray = floatArrayOf(
baselinex, ascentY, mWidth, ascentY,
baselinex, baselineY, mWidth, baselineY,
baselinex, dsecentY, mWidth, dsecentY,
baselinex, topY, mWidth, topY,
baselinex, bottomY, mWidth, bottomY
)
canvas?.drawLines(lineArray,paint)
}
效果图如下
![](https://img.haomeiwen.com/i7264052/907f166af93d8555.png)
1.4、常见函数
下面讲解下如何获取文字所占的宽度、高度以及最小矩形
![](https://img.haomeiwen.com/i7264052/c2163e706e7d6ef5.png)
灰色部分就是文字所占的宽度和高度,黑色部分就是所占的最小矩形。
1.4.1、高度
字符串所占的高度很容易计算,直接用bottom线所在的y坐标减去top线坐在的y坐标即可。
val fontMetrics = paint.getFontMetrics()
val bottom=baselineY+fontMetrics.bottom
val top=baselineY+fontMetrics.top
val height=bottom-top
1.4.2、宽度
宽度也非常容易得到,利用下面函数即可
public float measureText(String text)
示例如下
paint.textSize = 120f
val width = paint.measureText("harvic \\’ s blog")
1.4.3、最小矩形
最小矩形也是通过系统函数获取的,函数及其意义如下:
public void getTextBounds(String text, int start, int end, Rect bounds)
这个函数的作用是获取指定字符串的最小矩形,以(0,0)所在位置为基线
- String text:要测量的最小矩形字符串
- int start:要测量的起始字符在字符串中的索引
- int end:要测量的字符结束的位置
- Rect bounds:接收测量的结果
示例:
canvas?.drawText(str, 0f, 200f, paint)
paint.getTextBounds(str, 0, str.length, dstRect)
Log.e(javaClass.simpleName,"${dstRect.toShortString()}")
paint.style = Paint.Style.STROKE
paint.setColor(Color.BLACK)
canvas?.drawRect(dstRect, paint)
打印log如下:
PaintView: [8,-90][547,26]
发现最小矩形左上角的点的y坐标为负值,这是因为getTextBounds()获取的最小矩形是以(0,0)所在位置为基线,所以为负值。
看下面的原理图
![](https://img.haomeiwen.com/i7264052/175b5e4105daa024.png)
从图中可知,只需要向下平移baselineY的距离即可,修改上面的代码即可正确的绘制出矩形实际的位置
canvas?.drawText(str, 0f, 200f, paint)
//增加平移代码
canvas?.translate(0f,200f)
paint.getTextBounds(str, 0, str.length, dstRect)
Log.e(javaClass.simpleName,"${dstRect.toShortString()}")
paint.style = Paint.Style.STROKE
paint.setColor(Color.BLACK)
canvas?.drawRect(dstRect, paint)
效果图如下
![](https://img.haomeiwen.com/i7264052/1ae19ba44f85235c.png)
1.5、示例
1.5.1、给定左上角点绘图
假定给出所有绘制文字的左上角点,然后画出文字,先来看一张图片
![](https://img.haomeiwen.com/i7264052/0a6f4d13b5d9f800.png)
图中红色点就为给出的点,我们可以利用这个点,计算出基线的y坐标baselineY。
上面学习了FontMetrics,可得到一个公式
val fontMetrics = paint.getFontMetrics()
fontMetrics.top = mTop - baseLineY
所以基线的Y坐标为
val baseLineY = mTop - fontMetrics.top
最终代码如下
//画top线
canvas?.drawLine(0f,mTop,width.toFloat(),mTop,paint)
val fontMetrics = paint.getFontMetrics()
val baseLineY = mTop - fontMetrics.top
//画基线
canvas?.drawLine(0f,baseLineY,width.toFloat(),baseLineY,paint)
//绘制文字
canvas?.drawText(str, 0f, baseLineY, paint)
1.5.2、给定中间线绘制文字
加上文字所在矩形的中间线,目前存在下面几条线,如下图:
![](https://img.haomeiwen.com/i7264052/96e033975539cb1c.png)
从上图进行推导baseline的y坐标,过程如下:
A=C=(bottom.y-top.y)/2 即A=C=(FontMetrics.bottom-FontMetrics.top)/2
C=(bottom.y-baseline.y)+B 即C=FontMetrics.bottom+B
B=baseline.y-center.y
则C=FontMetrics.bottom+baseline.y-center.y
即(FontMetrics.bottom-FontMetrics.top)/2=FontMetrics.bottom+baseline.y-center.y
则baseline.y=(-FontMetrics.top-FontMetrics.bottom)/2+center.y
计算出baseline.y之后就可以计算出top.bottom了
val top = fontMetrics.top + baselineY
val bottom=fontMetrics.bottom+baselineY
具体代码如下:
val fontMetrics = paint.getFontMetrics()
//画基线
val baselineY = (-fontMetrics.top - fontMetrics.bottom) / 2 + mCenter
canvas?.drawLine(0f, baselineY, width.toFloat(), baselineY, paint)
//画top线
val mTop = fontMetrics.top + baselineY
canvas?.drawLine(0f, mTop, width.toFloat(), mTop, paint)
//画center
canvas?.drawLine(0f, mCenter, width.toFloat(), mCenter, paint)
//画bottom
val mBottom = fontMetrics.bottom + baselineY
canvas?.drawLine(0f, mBottom, width.toFloat(), mBottom, paint)
//画文字
canvas?.drawText(str, 0f, baselineY, paint)
效果如下
![](https://img.haomeiwen.com/i7264052/c23945534ed97a02.png)
2、Paint常用函数
- setStrokeMiter(float miter):设置画笔的倾斜度,并没有什么用处
-
PathEffect setPathEffect(PathEffect effect):设置路径的样式,参数是派生自PathEffect左右子类:ComposePathEffect、CornerPathEffect、DashPathEffect、DiscretePathEffect、PathDashPathEffect、SumPathEffect,能实现复杂的路径效果,每种路径效果对应下图
image.png
-
public void setStrokeCap(Cap cap):设置线帽样式,取值有三个:BUTT(无线帽)、ROUND(圆形线帽)、SQUARE(方形线帽)。
image.png
-
public void setStrokeJoin(Join join):设置路径转角的样式,取值有三个:MITER(结合处为锐角)、ROUND(结合处为圆弧)、BEVEL(结合处为直线)
image.png
网友评论