上一篇安卓自定义view-Paint 画笔 已经对画笔的经常使用 api 进行进行阐述总结,这一片主要讨论安卓关于文字的处理方法。也是属于画笔的范畴。canvas
. 构造方法api
在安卓中绘制文字相关,有专门的处理画笔。bash
mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
复制代码
. 设置文字大小dom
mTextPaint.setTextSize(30);
复制代码
. 设置文本对齐方式post
文本对齐方式有三中分别是: LEFT(左对齐)、CENTER(居中对齐)、RIGHT(右对齐), 用过 office 办公软件的应该都不会陌生。测试
mTextPaint.setTextAlign(Paint.Align.LEFT);
canvas.drawText(text, (float)(mWidth / 2 - width / 2),
400,
mTextPaint);
mTextPaint.setTextAlign(Paint.Align.CENTER);
canvas.drawText(text, (float)(mWidth / 2 - width / 2),
400 + height,
mTextPaint);
mTextPaint.setTextAlign(Paint.Align.RIGHT);
canvas.drawText(text, (float)(mWidth / 2 - width / 2) ,
400 + 2 * height,
mTextPaint);
复制代码
. 设置地区文字语言字体
不过我在真机上测试并无任何变化,个人真机只有简体、繁体、中文切换。模拟器测试野咩用。 有大佬知道的好心讲解一下👍。ui
mTextPaint.setTextLocale(Locale.CHINA);
canvas.drawText(text, (float)(mWidth / 2 - width / 2),
400,
mTextPaint);
mTextPaint.setTextLocale(Locale.TAIWAN);
canvas.drawText(text, (float)(mWidth / 2 - width / 2),
400 + height,
mTextPaint);
复制代码
. 设置文字水平缩放spa
大于 1 边宽,小于 1 变窄, 等于 1 没有变化。3d
mTextPaint.setTextScaleX(3.0f);
复制代码
. 设置文字错切
即让文字有必定倾斜角度, 大于 0 逆时针, 小于 0 顺时针。
mTextPaint.setTextSkewX(0.5f);
canvas.drawText(text, (float)(mWidth / 2 - width / 2),
400,
mTextPaint);
mTextPaint.setTextSkewX(-0.5f);
canvas.drawText(text, (float)(mWidth / 2 - width / 2),
400 + height,
mTextPaint);
复制代码
. 设置下划线
mTextPaint.setUnderlineText(true);
canvas.drawText(text, (float)(mWidth / 2 - width / 2),
400,
mTextPaint);
复制代码
. 设置删除线
mTextPaint.setStrikeThruText(true);
canvas.drawText(text, (float)(mWidth / 2 - width / 2),
400,
mTextPaint);
复制代码
. 设置文字加粗
mTextPaint.setFakeBoldText(true);
canvas.drawText(text, (float)(mWidth / 2 - width / 2),
400,
mTextPaint);
mTextPaint.setFakeBoldText(false);
canvas.drawText(text, (float)(mWidth / 2 - width / 2),
400 + height,
mTextPaint);
复制代码
. 设置文字间距
mTextPaint.setLetterSpacing(1.5f);
复制代码
. drawTextOnPath(@NonNull String text, @NonNull Path path, float hOffset,float vOffset, @NonNull Paint paint)
根据路径绘制文本内容。
mPath = new Path();
for (int i = 0; i < 10; i++) {
mPath.lineTo((float)(Math.random() * 500 + i * 35), (float)(Math.random() * 500 + 250));
}
canvas.drawPath(mPath, mPaint);
canvas.drawTextOnPath(text, mPath, 100, 0, mTextPaint);
复制代码
. StaticLayout
用来显示多行文本,它是一个容器,经过自身的 draw() 进行文本的绘制。StaticLayout 支持换行,它既能够为文字设置宽度上限来让文字自动换行,也会在换行符 \n 处主动换行。
private String text = "滚滚长江东逝水, 浪花淘尽英雄,是非成败转头, 青山依旧在,几度夕阳红";
layout = new StaticLayout(text, mTextPaint,
mWidth,
Layout.Alignment.ALIGN_NORMAL,
1.0f,
0.0f,
false);
layout.draw(canvas);
复制代码
. setTypeface(Typeface typeface) 设置字体
private String text = "滚滚长江东逝水, 浪花淘尽英雄,是非成败转头, 青山依旧在,几度夕阳红";
mTextPaint.setTypeface(Typeface.createFromAsset(getContext().getAssets(), "ygyqianmt.ttf"));
layout = new StaticLayout(text, mTextPaint,
mWidth,
Layout.Alignment.ALIGN_NORMAL,
1.0f,
0.0f,
false);
layout.draw(canvas);
复制代码
既然是测量文字,必然要知道安卓中是怎样表示文字的,这个世界上不一样的文字繁多,必然须要了解安卓系统是怎么知足不一样的文字的显示问题的。请看下面的图,这就是安卓系统文字的描述,对应的类是 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;
}
复制代码
来解释说明下这几个值的含义,首先看 Baseline,这个叫基准线,为何要设置这个呢?说白了就是为了排版更加美观。咱们最后一张图,基准线红色线到上面的绿色线之间的距离为上坡度 ascent, 基准线到下面蓝色线的距离为下坡度 descent。可是有的文字有相似与中文拼音的标点符号,因此须要额外预留一个空间,在上面的部分从 ascent 到顶部还有一个空间称为 top, 下面也是同样称为 bottom。这就说明了 top 应该是要比 ascent 要大一点。bottom 比 descent 要大。leading 为上一个文字的 bottom 到当前文字的 top 之间的距离。 咱们能够在代码中打印看看。
Paint.FontMetrics fontMetrics = mTextPaint.getFontMetrics();
Log.i(TAG, "top: " + fontMetrics.top);
Log.i(TAG, "ascent: " + fontMetrics.ascent);
Log.i(TAG, "descent: " + fontMetrics.descent);
Log.i(TAG, "bottom: " + fontMetrics.bottom);
canvas.drawText(text, (float)(mWidth / 2 - width / 2),
400,
mTextPaint);
复制代码
是否是跟描述的一致,还发如今安卓中基准线如下为正,基准线以上为负数。
. float getFontSpacing()
获取文本行间距,它的计算为 descent - ascent 之间的距离,而不是 bottom - top + leading,这个值是要比前面计算的要大的。为了让文字排版更加好看,而不至于间距过大,因此选择前面的方式计算。
private String text1 = "少小离家老大回";
private String text2 = "乡音未改鬓毛衰";
private String text3 = "儿童相见不相识";
private String text4 = "笑问客从何处来";
canvas.drawText(text1, 300, 300, mTextPaint);
canvas.drawText(text2, 300, 300 + mTextPaint.getFontSpacing(), mTextPaint);
canvas.drawText(text3, 300, 300 + 2 * mTextPaint.getFontSpacing(), mTextPaint);
canvas.drawText(text4, 300, 300 + 3 * mTextPaint.getFontSpacing(), mTextPaint);
复制代码
咱们能够看看 getFontSpacing() 的源码,能够发现它内部是经过 getFontMetrics 来计算的。
public float getFontSpacing() {
return getFontMetrics(null);
}
复制代码
.FontMetircs getFontMetrics()
在前面一个方法中已经看到过它的身影啦!也是经过这个方法拿到 descent、ascent、top、bottom、leading 等值。咱们这里打印一下 bottom - top + leading, descent - ascent 的值。
Paint.FontMetrics fontMetrics = mTextPaint.getFontMetrics();
Log.i(TAG, "bottom - top + leading: " + (fontMetrics.bottom - fontMetrics.top + fontMetrics.leading));
Log.i(TAG, "descent - ascent: " + (fontMetrics.descent - fontMetrics.leading));
Log.i(TAG, "getFontSpacing: " + mTextPaint.getFontSpacing());
复制代码
这样验证了前面所说的,getFontSpacing() 的计算为 descent - ascent
. getTextBounds(String text, int start, int end, Rect bounds)
从字面意义可知,这个是用来获取文字的范围的。会将计算的值存储到一个 rect 中。能够经过 rect 去获取文本的宽和高。
mTextPaint.getTextBounds(text1, 0, text1.length(), mTextRect);
canvas.drawText(text1, 300, 300, mTextPaint);
mPaint.setColor(Color.BLACK);
canvas.drawRect(mTextRect.left + 300,
mTextRect.top + 300,
mTextRect.right + 300,
mTextRect.bottom + 300, mPaint);
复制代码
. getTextBounds(char[] text, int index, int count, Rect bounds)
mTextPaint.getTextBounds(chars, 0, chars.length, mTextRect);
canvas.drawText(chars, 0, chars.length - 1, 300, 300, mTextPaint);
mPaint.setColor(Color.BLACK);
canvas.drawRect(mTextRect.left + 300,
mTextRect.top + 300,
mTextRect.right + 300,
mTextRect.bottom + 300, mPaint);
复制代码
. float measureText(String text) 测量文本宽度
mTextPaint.getTextBounds(chars, 0, chars.length, mTextRect);
Log.i(TAG, "width: " + mTextRect.width());
Log.i(TAG, "height: " + mTextRect.height());
float measureTextWidth = mTextPaint.measureText(chars, 0, chars.length);
Log.i(TAG, "measureTextWidth: " + measureTextWidth);
复制代码
能够看到使用 measureText 要比 getTextBounds 要大一点,这是由于文字之间有间隙,加上文字间距后再看效果。
mTextPaint.setLetterSpacing(1.5f);
mTextPaint.getTextBounds(chars, 0, chars.length, mTextRect);
Log.i(TAG, "width: " + mTextRect.width());
Log.i(TAG, "height: " + mTextRect.height());
float measureTextWidth = mTextPaint.measureText(chars, 0, chars.length);
Log.i(TAG, "measureTextWidth: " + measureTextWidth);
canvas.drawText(chars, 0, chars.length, 300, 300, mTextPaint);
mPaint.setColor(Color.BLACK);
canvas.drawRect(mTextRect.left + 300,
mTextRect.top + 300,
mTextRect.right + 300,
mTextRect.bottom + 300, mPaint);
复制代码
是否是发现变大了许多,这就是有间距的区别。
. int getTextWidths(char[] text, int index, int count, float[] widths)
获取指定字符的宽度,至关与对每一个字符执行 measureText。将测量到的字符宽度存放到 widths 中,它的变体方法也是相似的。
mTextPaint.getTextWidths(chars, 0, 3, widths);
Log.i(TAG, "width: " + widths[0]);
复制代码
. int breakText(char[] text, int index, int count,float maxWidth, float[] measuredWidth)
int number = mTextPaint.breakText(chars, 0, chars.length, 400, measureWidths);
Log.i(TAG, "breakText width: " + measureWidths[0]);
canvas.drawText(chars, number, chars.length - number, 300, 300, mTextPaint);
mPaint.setColor(Color.BLACK);
canvas.drawRect(mTextRect.left + 300,
mTextRect.top + 300,
mTextRect.right + 300,
mTextRect.bottom + 300, mPaint);
复制代码
. getRunAdvance(CharSequence text, int start, int end, int contextStart, int contextEnd, boolean isRtl, int offset)
计算光标位置, 在 API 23 后引入。这几个参数意思:start 起始字符位置,end 字符结束位置, contextStart: 上下文的字符其实起始位置, contextEnd: 上下文字符结束位置。 文字方向,从左往右或从右往左, offset: 须要测量的字符个数
必须符合这个条件
0 <= contextStart <= start <= offset <= end <= contextEnd <= text.length
复制代码
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
float runAdvance = mTextPaint.getRunAdvance(chars,
0,
3,
0,
chars.length,
false,
2);
Log.i(TAG, "runAdvance: " + runAdvance);
mPaint.setColor(Color.BLACK);
mPaint.setStrokeWidth(3);
canvas.drawLine(mTextRect.left + runAdvance + 300, mTextRect.top + 300,
mTextRect.left + runAdvance + 300, mTextRect.bottom + 300, mPaint);
}
复制代码
mTextPaint.getTextBounds(text, 0, text.length(), mTextRect);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
float runAdvance = mTextPaint.getRunAdvance(text,
0,
text.length(),
0,
text.length(),
false,
text.length());
mPaint.setColor(Color.BLACK);
mPaint.setStrokeWidth(3);
canvas.drawLine(mTextRect.left + 300 + runAdvance,
mTextRect.top + 300,
mTextRect.left + 300 + runAdvance,
mTextRect.bottom + 300,
mPaint);
}
canvas.drawText(text, 300, 300, mTextPaint);
复制代码
.hasGlyph(String string)
检查是否相同字形。
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
boolean b = mTextPaint.hasGlyph("\uD83C\uDDE8\uD83C\uDDF3");
boolean aa = mTextPaint.hasGlyph("aa");
boolean ab = mTextPaint.hasGlyph("ab");
Log.i(TAG, "b: " + b);
Log.i(TAG, "aa: " + aa);
Log.i(TAG, "ab: " + ab);
}
复制代码
因为两个不一样字符串组合不算字形。因此是相同的。