[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)
```
- 介绍
- UI
- MaterialButton
- MaterialButtonToggleGroup
- 字体相关设置
- Material Design
- Toolbar
- 下拉刷新
- 可折叠式标题栏
- 悬浮按钮
- 滑动菜单DrawerLayout
- NavigationView
- 可交互提示
- CoordinatorLayout
- 卡片式布局
- 搜索框SearchView
- 自定义View
- 简单封装单选
- RecyclerView
- xml设置点击样式
- adb
- 连接真机
- 小技巧
- 通过字符串ID获取资源
- 自定义View组件
- 使用系统控件重新组合
- 旋转菜单
- 轮播图
- 下拉输入框
- 自定义VIew
- 图片组合的开关按钮
- 自定义ViewPager
- 联系人快速索引案例
- 使用ListView定义侧滑菜单
- 下拉粘黏效果
- 滑动冲突
- 滑动冲突之非同向冲突
- onMeasure
- 绘制字体
- 设置画笔Paint
- 贝赛尔曲线
- Invalidate和PostInvalidate
- super.onTouchEvent(event)?
- setShadowLayer与阴影效果
- Shader
- ImageView的scaleType属性
- 渐变
- LinearGradient
- 图像混合模式
- PorterDuffXfermode
- 橡皮擦效果
- Matrix
- 离屏绘制
- Canvas和图层
- Canvas简介
- Canvas中常用操作总结
- Shape
- 圆角属性
- Android常见动画
- Android动画简介
- View动画
- 自定义View动画
- View动画的特殊使用场景
- LayoutAnimation
- Activity的切换转场效果
- 属性动画
- 帧动画
- 属性动画监听
- 插值器和估值器
- 工具
- dp和px的转换
- 获取屏幕宽高
- JNI
- javah命令
- C和Java相互调用
- WebView
- Android Studio快捷键
- Bitmap和Drawable图像
- Bitmap简要介绍
- 图片缩放和裁剪效果
- 创建指定颜色的Bitmap图像
- Gradle本地仓库
- Gradle小技巧
- RxJava+Okhttp+Retrofit构建网络模块
- 服务器相关配置
- node环境配置
- 3D特效