目录
一、概述
本篇主要介绍Canvas绘制文字的相关内容。Canvas绘制文字的相关方法,如下:
public void drawText(String text, float x, float y, @NonNull Paint paint);
public void drawText(char[] text, int index, int count, 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 drawTextOnPath(char[] text, int index, int count, @NonNull Path path,
float hOffset, float vOffset, Paint paint)
public void drawTextOnPath(String text, @NonNull Path path, float hOffset,
float vOffset, Paint paint)
public void drawTextRun(char[] text, int index, int count, int contextIndex,
int contextCount, float x, float y, boolean isRtl, Paint paint)
public void drawTextRun(CharSequence text, int start, int end, int contextStart,
int contextEnd, float x, float y, boolean isRtl, Paint paint)
public void drawPosText(char[] text, int index, int count, float[] pos, Paint paint)
public void drawPosText(String text, float[] pos, Paint paint)
Canvas绘制文字的方法一共四分为四类:
- drawText
- drawPostText
- drawTextOnPath
- drawTextRun
下面我们详细说它们的使用。
二、drawText
先看下基本的使用。代码:
public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setLayerType(LAYER_TYPE_SOFTWARE,null);
// 初始化画笔
initPaint();
}
private void initPaint() {
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setStrokeWidth(5);
mPaint.setColor(Color.DKGRAY);
mPaint.setTextSize(80);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
String text="Hello,安卓!";
canvas.drawText(text, 0,0, mPaint);
}
效果:
发现文字只显示下面一点点,这是为什么?
1、四格线和基线
其实这里的参数x,y并不是文字左上角顶点的位置,而是基线的起始位置。就像我们小时候学习写字母时,用的本子是四线格的,我们必须把字母按照规则写在四线格内。比如:
而canvas在利用drawText绘制文字时,也是有规则的,这个规则就是基线!我们先来看一下什么是基线:
在使用drawText绘制文字的时候会传入x,y两个参数,这两个参数不是指某一个点的坐标(当然为了好理解也可以作为一个点来看)。先看官方文档的解释:
@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
从上面的注释可以看出:x表示文本原点的x坐标;y表示文本基线的y坐标。为了理解方便将这两个坐标表示的点画出来。如图:
红点位置即是基线的起始位置,可见基线就是四线格中的第三条线。也就是说,只要基线的位置定了,那文字的位置必然是定了的!
代码:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
String text="Hello,安卓!";
canvas.drawText(text, 10,100, mPaint);
}
效果:
其实除了基线之外,绘制文字时还有几条线要关注下,它们被封装在Paint.FontMetrics类中。如下:
/**
* Class that describes the various metrics for a font at a given text size.
* Remember, Y values increase going down, so those values will be positive,
* and values that measure distances going up will be negative. This class
* is returned by getFontMetrics().
*/
public static class FontMetrics {
/**
* The maximum distance above the baseline for the tallest glyph in
* the font at a given text size.
*/
public float top;
/**
* The recommended distance above the baseline for singled spaced text.
*/
public float ascent;
/**
* The recommended distance below the baseline for singled spaced text.
*/
public float descent;
/**
* The maximum distance below the baseline for the lowest glyph in
* the font at a given text size.
*/
public float bottom;
/**
* The recommended additional space to add between lines of text.
*/
public float leading;
}
具体这几个变量代表的意思,大家可以自行百度
2、文字效果
绘制文字时,很多效果都可以通过Paint设置,如下:
/**
* 普通设置
*/
//设置画笔颜色
paint.setColor(Color.RED);
//设置画笔宽度
paint.setStrokeWidth (5);
//指定是否使用抗锯齿功能,如果使用,会使绘图速度变慢
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);
//设置字体水平倾斜度,普通斜体字是-0.25
paint.setTextSkewX((float) -0.25);
//设置带有删除线效果
paint.setStrikeThruText(true);
//只会将水平方向拉伸,高度不会变
paint.setTextScaleX(2);
/**
* 字体设置
*/
paint.setTypeface(typeface);
看下效果:
3、文字对齐方式(setTextAlign)
通过Paint.setTextAlign方法可以设置文字的对齐方式,共有三种:
- Paint.Align.LEFT
- Paint.Align.RIGHT
- Paint.Align.CENTER
不过需要注意的是:这里的对齐方式是相对于基线的起始位置的。如下:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
String text="Hello,安卓!";
mPaint.setTextAlign(Paint.Align.LEFT);
canvas.drawText(text, 400,100, mPaint);
mPaint.setTextAlign(Paint.Align.RIGHT);
canvas.drawText(text, 400,200, mPaint);
mPaint.setTextAlign(Paint.Align.CENTER);
canvas.drawText(text, 400,300, mPaint);
}
红点为基线的起始位置,一般默认的对齐方式是:Paint.Align.LEFT。
4、字体相关(Typeface)
Typeface是专门用来设置字体样式的,通过paint.setTypeface()来指定。可以指定系统中的字体样式,也可以指定自定义的样式文件中获取。要构建Typeface时,可以指定所用样式的正常体、斜体、粗体等,如果指定样式中,没有相关文字的样式就会用系统默认的样式来显示,一般默认是宋体。
创建Typeface:
Typeface create(String familyName, int style) //直接通过指定字体名来加载系统中自带的文字样式
Typeface create(Typeface family, int style) //通过其它Typeface变量来构建文字样式
Typeface createFromAsset(AssetManager mgr, String path) //通过从Asset中获取外部字体来显示字体样式
Typeface createFromFile(String path)//直接从路径创建
Typeface createFromFile(File path)//从外部路径来创建字体样式
Typeface defaultFromStyle(int style)//创建默认字体
上面的各个参数都会用到Style变量,Style的枚举值如下:
Typeface.NORMAL //正常体
Typeface.BOLD //粗体
Typeface.ITALIC //斜体
Typeface.BOLD_ITALIC //粗斜体
4.1、使用系统中的字体
从上面创建Typeface的所有函数中可知,使用系统中自带的字体有下面两种方式来构造Typeface:
Typeface defaultFromStyle(int style)//创建默认字体:SANS_SERIF
Typeface create(String familyName, int style) //直接通过指定字体名来加载系统中自带的文字样式
其实,第一个仅仅是使用系统默认的样式来绘制字体。第二个构造函数可以指定Android系统支持的三种字体:SERIF、MONOSPACE、SANS_SERIF。下面看下实现:
代码:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
String text="Hello,安卓!";
mPaint.setTypeface(Typeface.create(SERIF,Typeface.NORMAL));
canvas.drawText(text, 10,100, mPaint);
mPaint.setTypeface(Typeface.create(MONOSPACE,Typeface.NORMAL));
canvas.drawText(text, 10,200, mPaint);
mPaint.setTypeface(Typeface.create(SANS_SERIF,Typeface.NORMAL));
canvas.drawText(text, 10,300, mPaint);
}
效果图:
可以看出android系统默认的字体是SANS_SERIF。
4.2、使用自定义字体
自定义字体的话,我们就需要从外部字体文件加载我们所需要的字体的,有三个重载方法:
Typeface createFromAsset(AssetManager mgr, String path) //通过从Asset中获取外部字体来显示字体样式
Typeface createFromFile(String path)//直接从路径创建
Typeface createFromFile(File path)//从外部路径来创建字体样式
看下具体使用:首先在Asset下建一个文件夹,命名为fonts,然后将字体文件hwxk.ttf 放入其中,使用上面的方法加载。
代码:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
String text="Hello,安卓!";
//根据路径得到Typeface
Typeface typeface=Typeface.createFromAsset(mContext.getAssets(), "fonts/hwxk.ttf");
mPaint.setTypeface(typeface);
canvas.drawText(text, 10,100, mPaint);
}
效果图:
5、文字截取
通过drawText另外三个重载方法可以实现文字的截取,如下
代码:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
String text="Hello! 安卓";
canvas.drawText(text,1,8,50,100,mPaint);
canvas.drawText(text.toCharArray(),1,8,50,200,mPaint);
}
效果图:
三、drawPosText
这类重载方法可以分别指定每个文字的位置,截取一部分文字绘制。但是坑很多:
- 必须指定所有字符位置,否则直接crash掉
- 性能不佳,在大量使用的时候可能导致卡顿
- 不支持emoji等特殊字符,不支持字形组合与分解
看下代码:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
String text="Hello 安卓!";
canvas.drawPosText(text.toCharArray(),0,9,new float[]{
100,100,
200,200,
300,300,
400,400,
500,500,
600,600,
700,700,
800,800,
900,900,
},mPaint);
}
效果图:
四、drawTextOnPath
根据方法名可以知道,这类重载方法根据路径绘制文字,代码实现:
public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setLayerType(LAYER_TYPE_SOFTWARE,null);
// 初始化画笔
this.mContext=context;
initPaint();
}
private void initPaint() {
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setStrokeWidth(5);
mPaint.setColor(Color.DKGRAY);
mPaint.setTextSize(80);
mPaint.setStyle(Paint.Style.STROKE);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
String text="Hello 安卓!";
Path paths = new Path();
RectF rectF = new RectF(100, 100, 400, 600);
paths.addArc(rectF, 180, 180);
canvas.drawPath(paths, mPaint);
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
canvas.drawTextOnPath(text, paths, 0, 0, mPaint);
}
效果图:
这个方法后面在分析Path时,在详细说明。
五、drawTextRun
drawTextRun(char[] text, int index, int count, int contextIndex, int contextCount, float x,
float y, boolean isRtl, Paint paint)
drawTextRun(CharSequence text, int start, int end, int contextStart, int contextEnd,
float x, float y, boolean isRtl, Paint paint)
drawTextRun是在 API 23 新加入的方法。它和drawText一样都是绘制文字,但加入了两项额外的设置:上下文和文字方向。用于辅助一些文字结构比较特殊的语言的绘制(例如阿拉伯语根据它相邻的两个文字的不同会拥有不同的形状)。在绘制英文和汉语时没有发现contextIndex和contextCount参数的效果。另外contextIndex和contextCount参数要还有限制:contextIndex不能比index大;contextIndex+contextCount不能比index+count小。否则报错。简单看下它的使用:
代码:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
String text="Hello 安卓!";
canvas.drawTextRun(text.toCharArray(), 2, 6, 0, 8, 20, 100, true, mPaint);
canvas.drawTextRun(text.toCharArray(), 2, 6, 1, 7, 20, 200, false, mPaint);
}
效果图:
六、总结
上面简单分析了一些Canvas绘制文字的方法,其中需要注意的是绘制文字基线的位置并不是一般的左上角。还有涉及Path的相关内容后面在研究。
祝:工作顺利!