前言:上一篇介绍了Canvas的基本操作,通过canvas来绘制各种图形:canvas基本操作,不过Canvas中还有两个比较复杂也比较重要的功能。分别是绘制文本(Text)和绘制路径(Path),今天就先看一下文本的绘制。先看下paint有哪些关于文本的设置:
paint.setTextAlign(Align.CENTER) :设置文字对齐方式
paint.setTextSize(12) :设置文字大小
paint.setFakeBoldText(true) :设置是否为粗体文字
paint.setUnderlineText(true) :设置下划线
paint.setTextSkewX((float) -0.25) :设置字体水平倾斜度,普通斜体字是 -0.25
paint.setStrikeThruText(true) :设置带有删除线效果
paint.setTextScaleX(2) :设置水平拉伸,高度不会变
好吧,这是针对绘制文本的一些设置。看下绘制绘制文本的api:
1、drawText(char[] text, int index, int count, float x, float y,Paint paint
2、drawText(String text, float x, float y, Paint paint)
3、drawText(String text, int start, int end, float x, float y,Paint paint)
4、drawText(CharSequence text, int start, int end, float x, float y,Paint paint)
这就是四个绘制Text的api。分别解释下每个api所传参数的意义:
1、分别传入字符数组、要绘制起始字符在数组中的index、绘制字符的个数,基线的x轴坐标、基线的y轴坐标、paint对象。
2、分别传入String字符串、基线的y轴坐标、paint对象。
3、分别传入String字符串、起始位置、结束位置、基线的y轴坐标、paint对象。比如“abcdefg”,start和end分别传入1和3,则会绘制bc。注意,不包含d,是半合区间[1,3)
4、和3一样,只不过第一个参数不同。
好了,于是我们愉快的执行第一个api,去绘制一个字符串:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//设置画笔
Paint paint = new Paint();
paint.setStyle(Paint.Style.FILL);
paint.setColor(ContextCompat.getColor(getContext(), R.color.colorAccent));
paint.setTextSize(50);
canvas.drawText("这是一个测试文本".toCharArray(), 1, 3, 0, 0, paint);
}
于是看到如下效果:
是绘制了,但是为什么就绘制了一点点呢?要不是放大点,几乎看不见-。-为啥呢?
刚我们在介绍绘制文字的几个api时,不止一次提到了 基线这个词,这是啥玩意啊。来看一幅图:
图中的其它线暂且不管,我们可以看基线,图中标注baseline的位置。我们刚刚设置基线的位置是在(0,0)位置,文字的绘制都是基于baseline的,但不代表文字都在baseline的上方,有时候会绘制在baseline的下方,所以我们刚看到的效果就是绘制在基线下方的那一部分,如果我们调整下基线的y轴坐标:
canvas.drawText("这是一个测试文本".toCharArray(), 1, 3, 0, 100, paint);
效果如下:
这样文字就能看得到了。既然说到基线。我们在绘制文字的时候容易走进一个误区: 传入的坐标点是所绘制文字的左上角坐标。这是很容易犯的错误,实际上传入的是基线的坐标点。
Align分别有三个值:
public enum Align {
LEFT
CENTER
RIGHT
}
啥意思,先看下代码:
paint.setTextAlign(Paint.Align.LEFT);
canvas.drawText("这是一个测试文本".toCharArray(), 1, 3, 100, 100, paint);
paint.setTextAlign(Paint.Align.CENTER);
canvas.drawText("这是一个测试文本".toCharArray(), 1, 3, 100, 200, paint);
paint.setTextAlign(Paint.Align.RIGHT);
canvas.drawText("这是一个测试文本".toCharArray(), 1, 3, 100, 300, paint);
paint.setColor(Color.parseColor("#00ff00"));
paint.setStrokeWidth(5);
canvas.drawLine(100,0,100,600,paint);
共绘制了三段文本,不同的是基线的位置不同,Align不同。为了区分,还绘制了一条绿线,用于标注基线所在的x坐标。分别看下效果就明白啥意思了:
如果指定Align为left,则文字绘制在基线x坐标的右侧;如果Align为center,则文字绘制在基线x坐标两侧;Align指定为right时,文字绘制在基线x坐标的左边。
最开始的时候,我们放了一张图,我们只解释了其中的baseline(基线),还有top、ascent、descent、bottom四个值。都被封装在FontMetrics这个类中,分别表示如下含义:
其实这几个值正常情况下用的比较少,正常情况下还是基线用的比较多,没有特殊需求的话,这几个值几乎用不到。不过这只是距离,如果要算出各个值的y轴坐标要怎么算呢?很简单,每个都和baseline的y轴左边有关系。
FontMetrics.ascent = ascent Y坐标 - baseline Y 坐标
FontMetrics.top = top Y 坐标 - baseline Y 坐标
FontMetrics.descent = descent Y 坐标 - baseline Y 坐标
FontMetrics.bottom = bottom Y 坐标 - baseline Y 坐标
有了这个公式,我们可以很轻松的发现FontMetrics.ascent和FontMetrics.top的值一定是负值,FontMetrics.descent和FontMetrics.bottom的值一定是正值。因为Android的坐标轴是从左到右、自上而下的。
FontMetrics对象的获取:
Paint.FontMetrics fontMetrics = paint.getFontMetrics();
float ascent = fontMetrics.ascent;
float descent = fontMetrics.descent;
float top = fontMetrics.top;
float bottom = fontMetrics.bottom;
好吧 下面,下面来绘制一个文字,并绘制出文字所有的位置线:
float baselineY = 100f;
canvas.drawText("这是一个测试文本".toCharArray(), 1, 3, 100, baselineY, paint);
//获取FontMetrics对象
Paint.FontMetrics fontMetrics = paint.getFontMetrics();
//绘制top线
paint.setColor(Color.GREEN);
canvas.drawLine(0,baselineY+fontMetrics.top,600,baselineY+fontMetrics.top,paint);
//绘制ascent线
paint.setColor(Color.BLUE);
canvas.drawLine(0,baselineY+fontMetrics.ascent,600,baselineY+fontMetrics.ascent,paint);
//绘制descent线
paint.setColor(Color.BLACK);
canvas.drawLine(0,baselineY+fontMetrics.descent,600,baselineY+fontMetrics.descent,paint);
//绘制bottom线
paint.setColor(Color.YELLOW);
canvas.drawLine(0,baselineY+fontMetrics.bottom,600,baselineY+fontMetrics.bottom,paint);
//绘制基线
paint.setColor(Color.RED);
canvas.drawLine(0,baselineY,600,baselineY,paint);
好吧,看下效果:
红色是基线,其中top、ascent的差别还是挺大的,bottom和descent几乎重叠了。
paint还提供了一个measureText()方法用于测量文字所占的宽度。
Log.e("TAG------",paint.measureText("这是一个测试文本")+"");
输出的结果如下:
2019-03-26 17:42:40.742 16367-16367/com.suning.ctspm E/TAG------: 400.0
这个宽度和你设置的文字大小有关系, paint.setTextSize()设置的值越大,宽度也越大。
paint中还封装了一个方法,用于获取文字的外切矩形,也叫最小矩形,方法如下。
/**
* 获取指定字符串所对应的最小矩形,以(0,0)点所在位置为基线
* @param text 要测量最小矩形的字符串
* @param start 要测量起始字符在字符串中的索引
* @param end 所要测量的字符的结束位置
* @param bounds 用于接收测量结果
*/
public void getTextBounds(String text, int start, int end, Rect bounds);
简单示例:
Rect rect = new Rect();
String text = "这是一个测试文本";
paint.getTextBounds(text, 0, text.length(), rect);
于是你会发现获得的矩形:
咦,宽度怎么是396,刚我们上面打印的不是400么?原因如下:
measureText实际上是字体整体宽度,即左右带有一定的宽度。
getTextBounds获得的是字符串的最小矩形区域。
反正measureText()获取的宽度是在getTextBounds()左右坐标差的基础上加了一点点而已,正常想要获取文字的宽度用measureText()即可,高度也同理。
不过这个矩形还不能直接拿来绘制,原因是这个矩形的坐标是以(0,0)为文字的基线的,如果想要正确的绘制出文字的外切矩形,还需要加上文字的基线坐标,完整代码:
Paint paint = new Paint();
paint.setStyle(Paint.Style.FILL);
paint.setColor(ContextCompat.getColor(getContext(), R.color.colorAccent));
paint.setTextSize(50);
String text = "这是一个测试文本";
//基线的X轴坐标
float baselineX = 100f;
//基线的y轴坐标
float baselineY = 100f;
canvas.drawText(text, baselineX, baselineY, paint);
Rect rect = new Rect();
paint.setColor(Color.GREEN);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(3);
//获取最小矩形并将矩形的信息,如left、top、right、bottom等存储到rect中
paint.getTextBounds(text, 0, text.length(), rect);
//绘制最小矩形,需要将最小矩形的left、top、right、bottom值加上基线所在的x坐标或y坐标
canvas.drawRect(rect.left+baselineX,rect.top+baselineY,rect.right+baselineX,rect.bottom+baselineY,paint);
结果如下:
欧了,终于搞完了,下一篇准备介绍下Path和贝塞尔曲线相关的api,完~