Android开发Android技术知识Android自定义View

Android文字绘制 -- 怎样理解“基线”

2018-06-15  本文已影响31人  瑞神Collection

2018-06-15

首先我们看看怎样在Android中绘制出文字 以及与可以方便我们理解基线的其它线条
如何在android的View控件中绘制文字:
重写View的onDraw方法 使用其Canvas类的方法 canvas.drawText();

@Override
protected void onDraw(Canvas canvas) {
    canvas.drawText("jaeger",measureWidth / 2,measureHeight / 2,texPaint);//文字

    canvas.drawLine(0,measureHeight / 2,measureWidth,measureHeight / 2,centerLineP);//屏幕中线

    canvas.drawLine(0,measureHeight / 2 + (top + bottom) / 2,measureWidth,measureHeight / 2 + (top + bottom) / 2,texCenterLineP);//文字中线

    canvas.drawLine(0,measureHeight / 2 + top,measureWidth,measureHeight / 2 + top,topLineP);//文字顶线

    canvas.drawLine(0,measureHeight / 2 + bottom,measureWidth,measureHeight / 2 + bottom,bottomLineP);//文字顶线

    canvas.drawLine(0,measureHeight / 2 - 400,measureWidth,measureHeight / 2 - 400,halfLineP);//屏幕上分隔线
    canvas.drawLine(0,measureHeight / 2 + 400,measureWidth,measureHeight / 2 + 400,halfLineP);//屏幕下分隔线
}

其中measureWidth与measureHeight是手机屏幕的宽和高

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    if (MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY)
        measureWidth = MeasureSpec.getSize(widthMeasureSpec);
    if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY)
        measureHeight = MeasureSpec.getSize(heightMeasureSpec);
    setMeasuredDimension(measureWidth,measureHeight);
}

参数初始化方法

private void init(){
    texPaint = new Paint();//文字画笔
    texPaint.setTextSize(300);
    texPaint.setColor(Color.BLACK);
    texPaint.setTextAlign(Paint.Align.CENTER);
    texPaint.setTypeface(Typeface.DEFAULT_BOLD);

    centerLineP = new Paint();//屏幕中线画笔
    centerLineP.setStrokeWidth(15);
    centerLineP.setColor(Color.RED);

    topLineP = new Paint();//文字顶线画笔
    topLineP.setStrokeWidth(15);
    topLineP.setColor(Color.GREEN);

    bottomLineP = new Paint();//文字底线画笔
    bottomLineP.setStrokeWidth(15);
    bottomLineP.setColor(Color.YELLOW);

    texCenterLineP = new Paint();//文字中线画笔
    texCenterLineP.setStrokeWidth(5);
    texCenterLineP.setColor(Color.BLACK);

    halfLineP = new Paint();//分隔线画笔
    halfLineP.setStrokeWidth(15);
    halfLineP.setColor(Color.MAGENTA);

    Paint.FontMetricsInt fontMetricsInt = texPaint.getFontMetricsInt();
    top = fontMetricsInt.top;
    bottom = fontMetricsInt.bottom;
    baseLine = (bottom - top) / 2 - bottom;//文字基线
}

最终获取的效果如下:


效果图

现在我们来谈谈所谓的文字“基线”是什么
文字的“基线”就如同是我们的英语写字用的“四线本” 实际上我们正式写字的时候为了整洁 都是有一条线的

文字

所以我们可以发现在“效果图”上 文字和上述何等相似:a e r等单字都排布在基线上 而j g却超出了基线 但是g这个字在上半部分也是老实地排布在基线上 这和我们写英文字时的习惯是一模一样的

android文字的绘制也以使用了同样的方法使绘制出的文字保证整齐划一地排布:给文字定了一条基准线 而我们上述通过Paint.FontMetricsInt 获取的top和bottom值就与基线有关

示例

图片来源:https://blog.csdn.net/hailuoli/article/details/78558594

在我的手机上 top 为 - 317 bottom 为 82 计算出基线(偏移)为117
这里的top和bottom的值 实际是基于基线而得 可理解为 以文字基线为准 向上平移317像素为绘制区域的顶 向下平移82像素为绘制区域的底(基线向上为负 向下为正)

已知基线与绘制区顶相距317 与底相距82 得绘制区高度 bottom - top:82 - (-317) = 399
绘制区中线高度为(bottom - top)/ 2 : 399 / 2 = 199 (int)
绘制区中线高度等价于 中线与绘制区底 之间的距离 这个距离减去 基线与绘制区底 之间的距离 就是中线与基线之间的距离 (bottom - top) / 2 - bottom : 199 - 82 = 117

那这个值和我们在android中绘制文字有什么关系呢?
关键在于 我们在上述代码中 drawText传入的y值是屏幕的高 / 2 因为我们希望文字可以出现在屏幕的中间 但是事与愿违 我们的文字很明显没有出居中于屏幕

--因为 drawText中传入的Y的实际意义是文字的基线所在的位置--

我们将文字的基线放在了屏幕中间 文字在绘制出来时当然不会居中于屏幕 -- 我们应该将文字的中线放在屏幕的中间 这样文字在绘制出来时就可以居中了

因为一开始我们传的y实际是文字基线将要所在的位置 现在我们想传入的y应该是中线的位置 上面我们得到中线和基线的距离是117 所以应该以之前的y + 117(baseLine 基线偏移值):

    canvas.drawText("jaeger",measureWidth / 2,measureHeight / 2 + baseLine,texPaint);
效果

当然 明白了基线的作用和使用方法 那我们已应该可以处理这种问题:

    canvas.drawText("jaeger",measureWidth / 2,0 + baseLine,texPaint);//文字
不完全显示

经常绘制文字发现考虑基线偏移是很常见的问题
这时应该这样:

    canvas.drawText("jaeger",measureWidth / 2,-top,texPaint);//文字

文字显示完整了


完全显示

当然 文字绘制的参考线不只有top 和 bottom,还有ascent;descent;leading 参见“示例”图

上一篇下一篇

猜你喜欢

热点阅读