💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
[toc] # 1. 相关预备知识 我们知道在Canvas 中,使用drawText()函数绘制文字。这里简单看下将要使用的方法: ~~~ /** * Draw the text, with origin at (x,y), using the specified paint. The origin is interpreted * based on the Align setting in the paint. * * @param text The text to be drawn * @param x The x-coordinate of the origin of the text being drawn * @param y The y-coordinate of the baseline of the text being drawn * @param paint The paint used for the text (e.g. color, size, style) */ public void drawText(@NonNull String text, float x, float y, @NonNull Paint paint) { super.drawText(text, x, y, paint); } ~~~ 绘制的文本为text,位置为(x,y)。这里的坐标(x,y)和平时中所遇到的不同,这里不是左上角坐标,而是文本的右下角位置,如下图所示: ![](https://img.kancloud.cn/e2/c5/e2c538874eda74b0473b630b883a14fa_701x221.png) 于此同时,在字体中存在四条逻辑的线,系统在绘制文字时还有 4 条线,分别是 ascent、descent、top 和 bottom。如下图所示: ![](https://img.kancloud.cn/21/34/2134bbf39058a42af4f8c42919ea72ce_389x214.png) 对应的这些逻辑值存储在FontMetrics中,且不是直接存储在其中的,而是经过了转换,也就是: ``` ascent = ascent 线的 y 坐标 - baseline 线的 y 坐标。 descent = descent 线的 y 坐标 - baseline 线的 y 坐标。 top = top 线的 y 坐标 - baseline 线的 y 坐标。 bottom = bottom 线的 y 坐标 - baseline 线的 y 坐标。 ``` 那么,如果我们要在自定义View中绘制出来这四条线,就需要基于baseline的坐标来进行计算,即: ~~~ // 绘制文本 val fontMetrics = mPaint.fontMetrics // startY这里就是baseline val ascent = fontMetrics.ascent + startY val bottom = fontMetrics.bottom + startY val top = fontMetrics.top + startY val descent = fontMetrics.descent + startY drawLine(startX, startY, startX + 300, startY, mPaint) // baseline drawLine(startX, ascent, startX + 300, ascent, mPaint) //ascent drawLine(startX, bottom, startX + 300, bottom, mPaint) // bottom drawLine(startX, top, startX + 300, top, mPaint) // top drawLine(startX, descent, startX + 300, descent, mPaint) // descent ~~~ # 2. 获取字体宽高 ## 2.1 获取字体宽度 可以利用画笔工具来测量所占据的宽度: ~~~ // 使用画笔工具来获取字体的宽度 val defaultWidth = mPaint.measureText(mContent) ~~~ ## 2.2 获取字体高度 ~~~ // 使用bottom线值减去top线值,得到字符串所占据的高度值 val bottom = mPaint.fontMetrics.bottom val top = mPaint.fontMetrics.top val defaultHeight = bottom - top ~~~ # 3. 获取刚好罩住字体的最小矩形 可以创建一个Rect矩形对象,然后使用Paint的getTextBounds方法来传入,进行测量: ~~~ // 获取刚好罩住字体的矩形大小 val rect = Rect() mPaint.getTextBounds(mContent, 0, mContent.length, rect) Log.e("TAG", "rect: ${rect.toShortString()}", ) ~~~ 其中,其参数分别为: ~~~ /** * Retrieve the text boundary box and store to bounds. * * Return in bounds (allocated by the caller) the smallest rectangle that * encloses all of the characters, with an implied origin at (0,0). * * @param text string to measure and return its bounds * @param start index of the first char in the string to measure * @param end 1 past the last char in the string to measure * @param bounds returns the unioned bounds of all the text. Must be allocated by the caller */ public void getTextBounds(String text, int start, int end, Rect bounds) ~~~ 也就是第二、三个参数分别为始终的字符下标位置,前闭后开区间。 这里分别打印一下测量的字体的宽高和这里矩形的坐标: ![](https://img.kancloud.cn/3c/d6/3cd60c9e7b65e0749e1de4f4dccd46f7_512x58.png) 对应的矩形坐标,左上角为(0,-52),右下角为(129,1)。因为在代码中我们并没有给 getTextBounds()函数传递基线位置,那它就是以(0,0)点所在位置为基线来得到这个最小矩形的,所以这个最小矩形的位置就是以(0,0)点所在位置为基线的结果。 所以实际的矩形应该是向下平移baseline的距离。这里可以在onDraw方法中将它绘制出来: ~~~ // 获取刚好罩住字体的矩形大小 val rect = Rect() mPaint.getTextBounds(mContent, 0, mContent.length, rect) val rectPaint = Paint() rectPaint.color = resources.getColor(R.color.rect, null) // #4400FF00 rectPaint.isDither = true rectPaint.isAntiAlias = true rectPaint.style = Paint.Style.FILL drawRect(rect.left.toFloat(), rect.top.toFloat() + startY, rect.right.toFloat(), rect.bottom.toFloat() + startY, rectPaint) ~~~ ![](https://img.kancloud.cn/76/5a/765ab56e95413dce7a8320a4331c86fd_111x61.png) # 4. 字体相关函数 设置文字大小: ``` setTextSize(float textSize) ``` 设置是否为粗体文字 ``` setFakeBoldText(boolean fakeBoldText) ``` 设置带有删除线效果 ``` setStrikeThruText(boolean strikeThruText) ``` 设置下画线 ``` setUnderlineText(boolean underlineText) ``` 设置对其方式 ``` setTextAlign(Paint.Align align) ``` 设置字体样式 ``` setTypeface(Typeface typeface) ```