原文地址:Greathfs的Android绘图系列(五)——绘制文本
这个系列主要是介绍下Android自定义View和Android绘图机制,自己能力有限,如果在介绍过程中有什么错误,欢迎指正
绘制方法
首先,我们看下绘制文本相关方法
// 第一种
public void drawText (String text, float x, float y, Paint paint)
public void drawText (String text, int start, int end, float x, float y, Paint paint)
public void drawText (CharSequence text, int start, int end, float x, float y, Paint paint)
public void drawText (char[] text, int index, int count, float x, float y, Paint paint)
// 第二种
public void drawPosText (String text, float[] pos, Paint paint)
public void drawPosText (char[] text, int index, int count, float[] pos, Paint paint)
// 第三种
public void drawTextOnPath (String text, Path path, float hOffset, float vOffset, Paint paint)
public void drawTextOnPath (char[] text, int index, int count, Path path, float hOffset, float vOffset, Paint paint)
这三种方式有什么区别吗?
第一种:基本的绘制方法,只能指定文本基线位置(基线x默认在字符串左侧,基线y默认在字符串下方)
第二种:可以分别指定每个文字的位置。
第三种:根据路径绘制文本
这里简单解释下基线
这是我们以前写英文的四线格,其中从上往下数第三条线就是基线!
这里再说下第一种绘制文本的参数问题:第一个参数是我们需要绘制的文本,第四个参数是我们的画笔,这两个不用多说,主要是第二和第三个参数的含义,这两个参数在不同的情况下的值还是不一样的,x默认是这个字符串的左边在屏幕的位置,如果设置了paint.setTextAlign(Paint.Align.CENTER);那就是字符的中心,y是指定这个字符baseline在屏幕上的位置,大家记住了,不要混淆,y不是这个字符中心在屏幕上的位置,而是baseline在屏幕上的位置!
绘制文本
Paint和第一种绘制方式
前面的文章简单介绍过Paint,这篇还是要再介绍点属性,因为我们再绘制文本的时候,Paint设置不同属性,那么绘制出的文本也不同
//基本设置
paint.setStrokeWidth (2);//设置画笔宽度
paint.setAntiAlias(true); //指定是否使用抗锯齿功能,如果使用,会使绘图速度变慢
paint.setStyle(Paint.Style.FILL);//绘图样式,对于设文字和几何图形都有效
//设置文字对齐方式,取值:align.CENTER、align.LEFT或align.RIGHT
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);//只会将水平方向拉伸,高度不会变
OK,我们分别看下效果
- 绘图样式不同效果
Paint一共有三种样式:填充,描边,填充和描边
Paint paint=new Paint();
paint.setColor(Color.BLUE); //设置画笔颜色
paint.setStrokeWidth (5);//设置画笔宽度
paint.setAntiAlias(true); //指定是否使用抗锯齿功能,如果使用,会使绘图速度变慢
paint.setTextSize(100);//设置文字大小
//绘图样式设置为填充
paint.setStyle(Paint.Style.FILL);
canvas.drawText("这是一条测试数据", 20,200, paint);
//绘图样式设置为描边
paint.setStyle(Paint.Style.STROKE);
canvas.drawText("这是一条测试数据", 20,400, paint);
//绘图样式设置为填充且描边
paint.setStyle(Paint.Style.FILL_AND_STROKE);
canvas.drawText("这是一条测试数据", 20,600, paint);
效果图:
- 文字样式不同效果
Paint paint=new Paint();
paint.setColor(Color.BLUE); //设置画笔颜色
paint.setStrokeWidth (5);//设置画笔宽度
paint.setAntiAlias(true); //指定是否使用抗锯齿功能,如果使用,会使绘图速度变慢
paint.setTextSize(100);//设置文字大小
paint.setStyle(Paint.Style.FILL);
//样式设置
//粗体
paint.setFakeBoldText(true);
//下划线
paint.setUnderlineText(true);
//删除线
paint.setStrikeThruText(true);
//不倾斜
canvas.drawText("这是一条测试数据",20,200,paint);
//向有倾斜
paint.setTextSkewX((float) -0.25);
canvas.drawText("这是一条测试数据",20,400,paint);
//向左倾斜
paint.setTextSkewX((float) 0.25);
canvas.drawText("这是一条测试数据",20,600,paint);
效果图
- 其它效果
Paint paint=new Paint();
paint.setColor(Color.BLUE); //设置画笔颜色
paint.setStrokeWidth (5);//设置画笔宽度
paint.setAntiAlias(true); //指定是否使用抗锯齿功能,如果使用,会使绘图速度变慢
paint.setTextSize(100);//设置文字大小
paint.setStyle(Paint.Style.FILL);//绘图样式,设置为填充
//变通样式字体
canvas.drawText("这是一条测试数据", 20,200, paint);
//水平方向拉伸两倍
paint.setTextScaleX(2);//只会将水平方向拉伸,高度不会变
canvas.drawText("这是一条测试数据", 20,400, paint);
//写在同一位置,不同颜色,看下高度是否看的不变
paint.setTextScaleX(1);//先还原拉伸效果
canvas.drawText("这是一条测试数据", 20,600, paint);
paint.setColor(Color.RED);
paint.setTextScaleX(2);//重新设置拉伸效果
canvas.drawText("这是一条测试数据", 20,600, paint);
效果图
OK,Paint关于绘制文本这部分的属性就介绍这些
第二种绘制方式
public void drawPosText (String text, float[] pos, Paint paint)
public void drawPosText (char[] text, int index, int count, float[] pos, Paint paint)
解释下参数: String text:要绘制的文字 char[] text:要绘制的文字数组 int index::第一个要绘制的文字的索引 int count:要绘制的文字的个数,用来算最后一个文字的位置,从第一个绘制的文字开始算起 float[] pos:每个字体的位置,同样两两一组,如{x1,y1,x2,y2,x3,y3……}
Paint paint=new Paint();
paint.setColor(Color.BLUE); //设置画笔颜色
paint.setStrokeWidth (5);//设置画笔宽度
paint.setAntiAlias(true); //指定是否使用抗锯齿功能,如果使用,会使绘图速度变慢
paint.setTextSize(100);//设置文字大小
//绘图样式设置为填充
paint.setStyle(Paint.Style.FILL);
canvas.drawPosText("Hello",new float[]{
100,100, // 第一个字符位置
200,200, // 第二个字符位置
300,300, // ...
400,400,
500,500
},paint);
效果图
第三种绘制方式
public void drawTextOnPath (String text, Path path, float hOffset, float vOffset, Paint paint)
public void drawTextOnPath (char[] text, int index, int count, Path path, float hOffset, float vOffset, Paint paint)
解释下参数 float hOffset : 与路径起始点的水平偏移距离 float vOffset : 与路径中心的垂直偏移量
这种设计到Path(路径),这个后面会写,就先看看小例子了解下就行
Paint paint=new Paint();
paint.setColor(Color.RED); //设置画笔颜色
paint.setStrokeWidth (5);//设置画笔宽度
paint.setAntiAlias(true); //指定是否使用抗锯齿功能,如果使用,会使绘图速度变慢
paint.setTextSize(45);//设置文字大小
paint.setStyle(Paint.Style.STROKE);//绘图样式,设置为填充
String string="Hello World";
//先创建两个相同的圆形路径,并先画出两个路径原图
Path circlePath=new Path();
circlePath.addCircle(220,200, 180, Path.Direction.CCW);//逆向绘制
canvas.drawPath(circlePath, paint);//绘制出路径原形
Path circlePath2=new Path();
circlePath2.addCircle(750,200, 180, Path.Direction.CCW);
canvas.drawPath(circlePath2, paint);//绘制出路径原形
paint.setColor(Color.GREEN);
//hoffset、voffset参数值全部设为0,看原始状态是怎样的
canvas.drawTextOnPath(string, circlePath, 0, 0, paint);
//第二个路径,改变hoffset、voffset参数值
canvas.drawTextOnPath(string, circlePath2, 80, 30, paint);
效果图
drawText()中原点坐标(x,y)
我们知道,我们在drawText(text, x, y, paint)中传进去的原点坐标(x,y);其中,y表示的基线的位置。那x代表什么呢?x代表所要绘制文字所在矩形的相对位置。相对位置就是指指定点(x,y)在在所要绘制矩形的位置。我们知道所绘制矩形的纵坐标是由Y值来确定的,而相对x坐标的位置,只有左、中、右三个位置了。也就是所绘制矩形可能是在x坐标的左侧绘制,也有可能在x坐标的中间,也有可能在x坐标的右侧。
定义在x坐标在所绘制矩形相对位置的函数是:
/**
* 其中Align的取值为:Panit.Align.LEFT,Paint.Align.CENTER,Paint.Align.RIGHT
*/
Paint::setTextAlign(Align align);
(1)setTextAlign(Paint.Align.LEFT)
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint=new Paint();
//设置基本属性
paint.setAntiAlias(true);
paint.setColor(Color.RED);
paint.setStyle(Paint.Style.FILL);
paint.setTextSize(120);
paint.setTextAlign(Paint.Align.LEFT);
// paint.setTextAlign(Paint.Align.RIGHT);
// paint.setTextAlign(Paint.Align.CENTER);
canvas.drawText("abcdefghijk", 200, 200, paint);
//绘制坐标系
paint.setColor(Color.BLUE);
canvas.drawLine(0, 200, getWidth(), 200, paint);
canvas.drawLine(200, 0, 200, getHeight(), paint);
}
看完效果图,我们应该知道了原点(x,y)在矩形的左侧,即矩形从(x,y)的点开始绘制
(2)setTextAlign(Paint.Align.RIGHT)
把上面的代码改一改
//画基线
//设置基本属性
paint.setAntiAlias(true);
paint.setColor(Color.RED);
paint.setStyle(Paint.Style.FILL);
paint.setTextSize(120);
// paint.setTextAlign(Paint.Align.LEFT);
paint.setTextAlign(Paint.Align.RIGHT);
// paint.setTextAlign(Paint.Align.CENTER);
canvas.drawText("abcdefghijk", 200, 200, paint);
//绘制坐标系
paint.setColor(Color.BLUE);
canvas.drawLine(0, 200, getWidth(), 200, paint);
canvas.drawLine(200, 0, 200, getHeight(), paint);
原点(x,y)应在所要绘制矩形的右侧,也就是说正个矩形都在(0,200)的左侧,所以我们看到的是什么都没有。
(3)setTextAlign(Paint.Align.RIGHT)
Paint paint=new Paint();
//设置基本属性
paint.setAntiAlias(true);
paint.setColor(Color.RED);
paint.setStyle(Paint.Style.FILL);
paint.setTextSize(120);
// paint.setTextAlign(Paint.Align.LEFT);
// paint.setTextAlign(Paint.Align.RIGHT);
paint.setTextAlign(Paint.Align.CENTER);
canvas.drawText("abcdefghijk", 200, 200, paint);
//绘制坐标系
paint.setColor(Color.BLUE);
canvas.drawLine(0, 200, getWidth(), 200, paint);
原点(x,y)就在所要绘制文字所在矩形区域的正中间,换句话说,系统会根据(x,y)的位置和文字所在矩形大小,会计算出当前开始绘制的点,以使原点(x,y)正好在所要绘制的矩形的正中间。
FontMetrics
绘制text的其他线
除了基线,系统在绘制Text时,还是有其它线的,如下图
从图中可以知道,除了基线,还有另外的四条线,它们分别是 top,ascent,descent和bottom,它们的含义分别为:
- top:可绘制的最高高度所在线
- bottom:可绘制的最低高度所在线
- ascent :系统建议的,绘制单个字符时,字符应当的最高高度所在线
- descent:系统建议的,绘制单个字符时,字符应当的最低高度所在线
FontMetrics介绍
既然还有四条线,那这些线的位置怎么计算呢?
Android提供了一个类:FontMetrics,这个类里面对应有成员变量
它们的计算方法如下
ascent = ascent线的y坐标 - baseline线的y坐标;//负数
descent = descent线的y坐标 - baseline线的y坐标;//正数
top = top线的y坐标 - baseline线的y坐标;//负数
bottom = bottom线的y坐标 - baseline线的y坐标;//正数
leading = top线的y坐标 - ascent线的y坐标;//负数
FontMetrics的这几个变量的值都是以baseLine为基准的,对于ascent来说,baseline线在ascent线之下,所以必然baseline的y值要大于ascent线的y值,所以ascent变量的值是负的。其他几个同理。
同样我们可以推算出:
ascent线Y坐标 = baseline线的y坐标 + fontMetric.ascent;
descent线Y坐标 = baseline线的y坐标 + fontMetric.descent;
top线Y坐标 = baseline线的y坐标 + fontMetric.top;
bottom线Y坐标 = baseline线的y坐标 + fontMetric.bottom;
绘制ascent,descent,top,bottom线
获取FontMetrics实例
Paint.FontMetrics fontMetrics=paint.getFontMetrics();
Paint.FontMetricsInt fontMetricsInt = paint.getFontMetricsInt();
区别:得到对象的成员变量的值一个为float类型,一个为int类型。
绘制
int baseLineY = 200;
int baseLineX = 0 ;
Paint paint = new Paint();
//写文字
paint.setColor(Color.GREEN);
paint.setTextSize(120); //以px为单位
paint.setTextAlign(Paint.Align.LEFT);
canvas.drawText("abcdefghijk", baseLineX, baseLineY, paint);
//计算各线在位置
Paint.FontMetrics fm = paint.getFontMetrics();
float ascent = baseLineY + fm.ascent;
float descent = baseLineY + fm.descent;
float top = baseLineY + fm.top;
float bottom = baseLineY + fm.bottom;
//画基线
paint.setColor(Color.RED);
canvas.drawLine(baseLineX, baseLineY, 3000, baseLineY, paint);
//画top
paint.setColor(Color.BLUE);
canvas.drawLine(baseLineX, top, 3000, top, paint);
//画ascent
paint.setColor(Color.GREEN);
canvas.drawLine(baseLineX, ascent, 3000, ascent, paint);
//画descent
paint.setColor(Color.BLACK);
canvas.drawLine(baseLineX, descent, 3000, descent, paint);
//画bottom
paint.setColor(Color.RED);
canvas.drawLine(baseLineX, bottom, 3000, bottom, paint);
获取文字宽度、高度和最小矩形
获取文字宽度
String text="abcdefghijk";
//文字宽度
float width = mPaint.measureText(text);
获取文字高度
float top = fontMetrics.top + baseLineY;
float bottom = fontMetrics.bottom + baseLineY;
//文字高度
float height= bottom - top; //注意top为负数
//文字中点y坐标
float center = (bottom - top) / 2;
获取文字最小矩形
/**
* 获取指定字符串所对应的最小矩形,以(0,0)点所在位置为基线
* @param text 要测量最小矩形的字符串
* @param start 要测量起始字符在字符串中的索引
* @param end 所要测量的字符的长度
* @param bounds 接收测量结果
*/
public void getTextBounds(String text, int start, int end, Rect bounds);
例子:
String text = "abcdefghijk";
Paint paint = new Paint();
//设置paint
paint.setTextSize(120); //以px为单位
Rect minRect = new Rect();
paint.getTextBounds(text,0,text.length(),minRect);
drawText实现中文垂直居中
drawText里的origin是以baseline为基准的,直接以目标矩形的bottom传进drawText,字符位置会偏下。
Rect rect = new Rect(50, 50, 1000, 200);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setStrokeWidth(3);
paint.setTextSize(80);
String testString = "abcdefghijk";
paint.setColor(Color.BLUE);
canvas.drawRect(rect, paint);
paint.setColor(Color.RED);
canvas.drawText(testString, rect.left, rect.bottom, paint);
所以我们需要自己计算下基线的位置
这是上面的一张图,我们知道,我们绘制的Text在FontMetrics.top和FontMetrics.bottom线之间,FontMetrics.top的数值是个负数,其绝对值就是字体绘制边界到baseline的距离。所以如果是把文字画在 FontMetrics高度的矩形中, drawText就应该传入 -FontMetrics.top。要画在targetRect的居中位置,baseline的计算公式就是:
targetRect.centerY() - (FontMetrics.bottom - FontMetrics.top) / 2 - FontMetrics.top
优化后即:
(targetRect.bottom + targetRect.top - fontMetrics.bottom - fontMetrics.top) / 2
Rect targetRect = new Rect(50, 50, 1000, 200);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setStrokeWidth(3);
paint.setTextSize(80);
String testString = "abcdefghijk";
paint.setColor(Color.BLUE);
canvas.drawRect(targetRect, paint);
paint.setColor(Color.RED);
Paint.FontMetricsInt fontMetrics = paint.getFontMetricsInt();
int baseline = (targetRect.bottom + targetRect.top - fontMetrics.bottom - fontMetrics.top) / 2;
paint.setTextAlign(Paint.Align.CENTER);
canvas.drawText(testString, targetRect.centerX(), baseline, paint);
结尾
Github地址
参考文章:
http://blog.csdn.net/harvic880925/article/details/50423762
http://blog.csdn.net/hursing/article/details/18703599