字体绘制的相关的api的理解记录,网上有很多文章看了也是云里雾里的,这里自己根据官方文档及自己绘制测试,记录下自己理解到的字体绘制的一些东西。
/**
* 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 绘制的文字
* @param x 开始文字绘制的横向x坐标
* @param y 开始文字绘制的纵向y坐标(Y坐标是基于文字的基线baseLine而言)
* @param paint 绘制文字的画笔
*/
public void drawText(@NonNull String text, float x, float y, @NonNull Paint paint)
1:如何确认绘制字体的x坐标
可能大多数人第一反应x坐标就是字体开始绘制的坐标,这个没毛病,但是这个要区分字体的绘制点是从左开始还是从右开始还是从中间开始
//字体绘制排列方式
public void setTextAlign(Align align)
Paint paint = new Paint();
paint.setTextSize(50);
paint.setTextAlign(Paint.Align.CENTER);
canvas.drawText("打扫打扫打扫打扫的", 540, 50, paint);
paint.setTextAlign(Paint.Align.LEFT);
canvas.drawText("打扫打扫打扫打扫的", 540, 110, paint);
paint.setTextAlign(Paint.Align.RIGHT);
canvas.drawText("打扫打扫打扫打扫的",540,160,paint);
本demo测试手机屏幕宽度是1080的故取中心值x=540,文字绘制时的方向,这个是 基于drawText(..)中x参数来说的方向
- -CNETER:以X为起点,然后文字左右两边开始绘制
-LEFT: 以X为起点,然后从文字从左往右绘制(默认方式)
-RIGHT: 以X为起点,然后文字从右往左绘制
总结:x坐标确认时,一定要结合字体绘制方向来确认,当然不设置setTextAlign就默认left
2:如何确认绘制字体的y坐标,绘制文字是基于baseLine是什么意思
官方文本的绘制是基于字体的baseLine来确认Y坐标的,暴力认为y=baseLine的值,如果想绘制出来的文字和官方提供的TextView布局出来的文字水平居中,那么就必须计算出baseLine的值,当然y轴坐标也可以随便传值,主要看你想要的效果
如图的几个属性官方规定的至于为什么要这么设计,去找谷歌
baseline:字符基线,绘制是基于他的坐标绘制的
ascent:字符最高点到baseline的推荐距离
top:字符最高点到baseline的最大距离
descent:字符最低点到baseline的推荐距离
bottom:字符最低点到baseline的最大距离
leading:行间距,即前一行的descent与下一行的ascent之间的距离
获取这几个属性的值
//baseLine以上为负值,以下为正值
Paint.FontMetricsInt fontMetrics = paint.getFontMetricsInt();
int top=fontMetrics.top
int ascent=fontMetrics.ascent
int descent=fontMetrics.descent
int bootom=fontMetrics.bottom
int leading=fontMetrics.leading
计算baseLine的坐标(计算是基于字体垂直居中整个控件)
- 计算baseLine就必须获取当前控件的高度,假设当前控件高度为viewHeight
- 字体的高度textHeight=-fontMetrics.top+fontMetrics.bottom,
(也有人计算用-fontMetrics.descent+fontMetrics.descent) - 字体bottomY=baseLineY+fontMetrics.bottom(baseLine的坐标加上fontMetrics.bottom的值就是bottomY的坐标)
- 字体垂直居中控件bottomY=viewHeight/2+textHeight/2(字体要居中于控件,那么就是在控件中线往上下距离相等,)
综上baseLineY+fontMetrics.bottom=viewHeight/2+textHeight/2
baseLineY=viewHeight/2+textHeight/2-fontMetrics.bottombaseLineY=viewHeight/2+(-fontMetrics.top+fontMetrics.bottom)/2-fontMetrics.bottom
3:绘制字体及相关api的理解
String mText="测试字体绘制yǔacfgqjqw";
Paint paint = new Paint();
paint.setColor(Color.parseColor("#00c0c7"));
paint.setTextSize(90);
Paint.FontMetricsInt fontMetrics = paint.getFontMetricsInt();
Paint.FontMetrics floatFontMetrics = paint.getFontMetrics();
int viewWidth = getWidth();
float measureText= paint.measureText(mText);
Rect rect = new Rect();
paint.getTextBounds(mText, 0, mText.length(), rect);
//这里字体绘制控件高度即字体的高度即top+bottom,通过如上公式计算出来y=-fontMetrics.top
//绘制出来的字体和官方布局出来的字体是水平居中的
canvas.drawText(mText, rect.left, -fontMetrics.top, paint);
- paint.measureText(mText);
- paint.getTextBounds(mText, 0, mText.length(), rect);
1:两个都是测量字体的宽度,虽然这两个测量是有差别的,但是整体的宽度误差其实是很小的,理论上getTextBounds测量的宽度是要小于measureText,但是经过实际的不同测试,这个也不完全成立,有时候getTextBounds比measureText测量出来略大的情况也是有的,但是推荐还是使用measureText来测量字体的宽度。
2:measureText网上说这个测量是带有字体间距的,getTextBounds是包裹字体的值,这种说话如果是单个字体能够完全体现出来,但是绘制一长段文字这个区别就很小了,如下数据说话
String mText=”测试字体绘制yǔacfgqjqw”; //如上代码绘制的这段字体,的数据
getTextBounds:测量出来的rect:(1, -74 - 1045, 19) width=1044
measureText:测量出来的宽度width=1041.0————————————–单个字体测试数据——————————————————–
测–measureTextWidth:90.0 getTextBounds:Rect(1, -73 - 84, 11)
试–measureTextWidth:90.0 getTextBounds:Rect(2, -73 - 89, 10)
字–measureTextWidth:90.0 getTextBounds:Rect(2, -74 - 87, 10)
体–measureTextWidth:90.0 getTextBounds:Rect(2, -74 - 89, 10)
绘–measureTextWidth:90.0 getTextBounds:Rect(2, -74 - 88, 11)
制–measureTextWidth:90.0 getTextBounds:Rect(3, -73 - 83, 10)
y–measureTextWidth:45.0 getTextBounds:Rect(1, -49 - 44, 19)
ǔ–measureTextWidth:90.0 getTextBounds:Rect(22, -67 - 68, 5)
a–measureTextWidth:49.0 getTextBounds:Rect(5, -49 - 45, 1)
c–measureTextWidth:47.0 getTextBounds:Rect(4, -49 - 44, 1
f–measureTextWidth:31.0 getTextBounds:Rect(3, -70 - 32, 0)
g–measureTextWidth:50.0 getTextBounds:Rect(4, -49 - 44, 19)
q–measureTextWidth:50.0 getTextBounds:Rect(4, -49 - 43, 18)
j–measureTextWidth:22.0 getTextBounds:Rect(-4, -70 - 15, 19)
q–measureTextWidth:50.0 getTextBounds:Rect(4, -49 - 43, 18)
w–measureTextWidth:68.0 getTextBounds:Rect(2, -48 - 66, 0)
如上单个字体的宽度累加:measureTextWidth:1042 ,getTextBounds的宽度:907
从单个字体的绘制可以解释measureTextWidth测量的宽度是带有字体间距的,而getTextBounds是获取字体包裹最小宽度
这里单个字measureTextWidth绘制的累加宽度其实是等于直接测试整段文字得到的宽度
而getTextBounds获取到的单个文字累加宽度,是远小于整体文字通过getTextBounds获取到的宽度的
stackoverflow关于这两个api的解释
4:总结
- 绘制文字的y轴即baseLine的计算,如果你文字不想垂直居中于控件绘制,那么计算方式请根据自己实际情况来
- 如果你绘制的文字的高度即你控件的高度,那么baseLine=top
- measureTextWidth与getTextBounds宽度还是建议使用measureTextWidth来测量
- 字体的高度:1:top+bottom(建议使用) 2:getTextBounds中返回的矩形rect.height 3:我们设置的字体大小