Paint的一个测量字体的内部类
FontMetrics
FontMetrics fm = mTextPaint.getFontMetrics();
/**
* 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;
}
这张图很简单但是也很扼要的说明了top,ascent,descent,bottom,leading这五个参数。首先我们要知道Baseline基线,在Android中,文字的绘制都是从Baseline处开始的,Baseline往上至字符最高处的距离我们称之为ascent(上坡度),Baseline往下至字符最底处的距离我们称之为descent(下坡度),而leading(行间距)则表示上一行字符的descent到该行字符的ascent之间的距离,top和bottom文档描述地很模糊,其实这里我们可以借鉴一下TextView对文本的绘制,TextView在绘制文本的时候总会在文本的最外层留出一些内边距,为什么要这样做?因为TextView在绘制文本的时候考虑到了类似读音符号,可能大家很久没写过拼音了已经忘了什么叫读音符号了吧……下图中的A上面的符号就是一个拉丁文的类似读音符号的东西:
top的意思其实就是除了Baseline到字符顶端的距离外还应该包含这些符号的高度,bottom的意思也是一样,一般情况下我们极少使用到类似的符号所以往往会忽略掉这些符号的存在,但是Android依然会在绘制文本的时候在文本外层留出一定的边距,这就是为什么top和bottom总会比ascent和descent大一点的原因。而在TextView中我们可以通过xml设置其属性android:includeFontPadding="false"去掉一定的边距值但是不能完全去掉。下面我们在Canvas上绘制一段文本并尝试打印文本的top,ascent,descent,bottom和leading:
class MyView extends View {
private Paint mPaint;
private FontMetrics mFontMetrics;
String text = "dfM★○€гежтkjǎíìǖл";
public MyView(Context context) {
super(context);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setTextSize(100);
//mPaint.setTypeface(Typeface.SERIF);
//TextSize和Typeface都会影响FontMetrics
mFontMetrics = mPaint.getFontMetrics();
mPaint.setColor(Color.BLACK);
Log.d("Font", "ascent:" + mFontMetrics.ascent);
Log.d("Font", "top:" + mFontMetrics.top);
Log.d("Font", "leading:" + mFontMetrics.leading);
Log.d("Font", "descent:" + mFontMetrics.descent);
Log.d("Font", "bottom:" + mFontMetrics.bottom);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.WHITE);
canvas.drawText(text, 0,Math.abs(mFontMetrics.top), mPaint);
}
}
我们把注释掉的打开
mPaint.setTypeface(Typeface.SERIF);
同样所有的值也改变了,那么我们知道这样的一个东西有什么用呢?如上所说文本的绘制是从Baseline开始,并且Baseline并非文本的分割线,当我们想让文本绘制的时候居中屏幕或其他的东西时就需要计算Baseline的Y轴坐标,比如我们让我们的文本居中画布
局部放大图
class MyView extends View {
private Paint textPaint,linePaint,tagPaint;
private FontMetrics mFontMetrics;
String TEXT = "dfM★○€гежтkjǎíìǖл";
private int baseX, baseY;// Baseline绘制的XY坐标
Rect bounds ;
public MyView(Context context) {
super(context);
textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
textPaint.setDither(true);
textPaint.setTextSize(120);
// textPaint.setTypeface(Typeface.SERIF);
//TextSize和Typeface都会影响FontMetrics
mFontMetrics = textPaint.getFontMetrics();
textPaint.setColor(Color.BLACK);
linePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
linePaint.setStyle(Paint.Style.STROKE);
linePaint.setStrokeWidth(1);
linePaint.setColor(Color.RED);
Log.d("Font", "ascent:" + mFontMetrics.ascent);
Log.d("Font", "top:" + mFontMetrics.top);
Log.d("Font", "leading:" + mFontMetrics.leading);
Log.d("Font", "descent:" + mFontMetrics.descent);
Log.d("Font", "bottom:" + mFontMetrics.bottom);
bounds= new Rect();
tagPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
tagPaint.setTextSize(50);
tagPaint.setStrokeWidth(20);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.WHITE);
// 计算Baseline绘制的起点X轴坐标
baseX = (int) (canvas.getWidth() / 2 - textPaint.measureText(TEXT) / 2);
// 计算Baseline绘制的Y坐标
baseY = (int) ((canvas.getHeight() / 2) - ((textPaint.descent() + textPaint.ascent()) / 2));
canvas.drawText(TEXT, baseX, baseY, textPaint);
canvas.drawLine(0, canvas.getHeight() / 2, canvas.getWidth(), canvas.getHeight() / 2, linePaint);
canvas.drawLine(canvas.getWidth()/2, 0, canvas.getWidth()/2, canvas.getHeight() , linePaint);
canvas.save();
canvas.translate(baseX, canvas.getHeight() / 2 - ((textPaint.descent() + textPaint.ascent()) / 2));
textPaint.getTextBounds(TEXT, 0, TEXT.length(), bounds);
canvas.drawRect(bounds, linePaint);
canvas.restore();
linePaint.setColor(Color.BLUE);
canvas.drawLine(baseX,baseY+mFontMetrics.top , baseX+textPaint.measureText(TEXT), baseY+mFontMetrics.top, linePaint);
tagPaint.setColor(Color.BLUE);
canvas.drawPoint(baseX, baseY+textPaint.descent() - textPaint.ascent(), tagPaint);
canvas.drawText("top",
baseX+20,
baseY+textPaint.descent() - textPaint.ascent()-(tagPaint.descent()+tagPaint.ascent())/2,
tagPaint);
linePaint.setColor(Color.GREEN);
canvas.drawLine(baseX,baseY+mFontMetrics.ascent , baseX+textPaint.measureText(TEXT), baseY+mFontMetrics.ascent, linePaint);
tagPaint.setColor(Color.GREEN);
canvas.drawPoint(baseX, baseY+textPaint.descent() - textPaint.ascent()+50, tagPaint);
canvas.drawText("ascent",
baseX+20,
baseY+textPaint.descent() - textPaint.ascent()+50-(tagPaint.descent()+tagPaint.ascent())/2,
tagPaint);
linePaint.setColor(Color.MAGENTA);
canvas.drawLine(baseX,baseY , baseX+textPaint.measureText(TEXT), baseY, linePaint);
tagPaint.setColor(Color.MAGENTA);
canvas.drawPoint(baseX, baseY+textPaint.descent() - textPaint.ascent()+100, tagPaint);
canvas.drawText("baseline",
baseX+20,
baseY+textPaint.descent() - textPaint.ascent()+100-(tagPaint.descent()+tagPaint.ascent())/2,
tagPaint);
linePaint.setColor(Color.parseColor("#545654"));
canvas.drawLine(baseX,baseY+mFontMetrics.descent , baseX+textPaint.measureText(TEXT), baseY+mFontMetrics.descent, linePaint);
tagPaint.setColor(Color.parseColor("#545654"));
canvas.drawPoint(baseX, baseY+textPaint.descent() - textPaint.ascent()+150, tagPaint);
canvas.drawText("descent",
baseX+20,
baseY+textPaint.descent() - textPaint.ascent()+150-(tagPaint.descent()+tagPaint.ascent())/2,
tagPaint);
linePaint.setColor(Color.CYAN);
canvas.drawLine(baseX,baseY+mFontMetrics.bottom , baseX+textPaint.measureText(TEXT), baseY+mFontMetrics.bottom, linePaint);
tagPaint.setColor(Color.CYAN);
canvas.drawPoint(baseX, baseY+textPaint.descent() - textPaint.ascent()+200, tagPaint);
canvas.drawText("bottom",
baseX+20,
baseY+textPaint.descent() - textPaint.ascent()+200-(tagPaint.descent()+tagPaint.ascent())/2,
tagPaint);
TextPaint textPaint = new TextPaint(Paint .ANTI_ALIAS_FLAG);
textPaint.setTextSize(30);
StaticLayout staticLayout =new StaticLayout(
"StaticLayout is a Layout for text that will not be edited after it is laid out. Use {@link DynamicLayout} for text that may change. This is used by widgets to control text layout. You should not need to use this class directly unless you are implementing your own widget or custom display object, or would be tempted to call",
textPaint,
canvas.getWidth()/2,
Alignment.ALIGN_NORMAL, 1.0F, 0.0F, false);
staticLayout.draw(canvas);
}
}