A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

    大家在工作过程中经常会遇到需要自定义控件的需求,有时候需要在View里面自己绘制文字。而在Android中文字的绘制还是有一些坑在里面的,今天我们就一起探讨一下自定义View中文字绘制相关的问题,特别是文字高度的求法,很容易就会弄错。    首先,如果想要在View中绘制文字,势必要用到一个API就是Canvas.drawText(String text, float x, float y, Paint paint)。这个API大家如果自己尝试使用的话,猜一猜参数列表的含义:第1个是要绘制的文字,很容易理解;第2、3个看上去像是文字的坐标,按照惯例,应该是基于View的左上角的偏移量(当然实际上不是,我们后面再说);第4个参数是画笔,这个就不多说了。然后我们按照自己理解的写一下试试,果然错了,文字的坐标并不正确。那究竟是怎么回事呢?实际上,安卓中文字的绘制是基于baseline的最左边为原点的。那么什么是baseline呢?我们看一下下面这张图。
   
    是不是有点熟悉,我们刚开始学外语写英文的时候都在四线格里写,第三条线就类似于baseline的存在。注意看小写的“q”和“g”,是不是直接伸到baseline下方了,这条线就是基准线。除了这条线之外,图中还有几个概念,我们一起来说一下:
                baseLine:一行文字的基准线
                Ascent: 字符顶部到baseLine的距离
                Descent: 字符底部到baseLine的距离
                Leading: 字符行间距

    这样的话,我们想话一个文字,基于基准线最左方为原点,是不是就是文字的高度的负值为y了,所以引出一个问题:文字的高度如何得到?
    观察图片的话,很容易得到文字的高度就是descent - ascent的值。我们写一段代码来验证一下。
[AppleScript] 纯文本查看 复制代码
public class MyView extends View {
    private static final String TAG = "MyView";

    public MyView(Context context) {
        super(context);
    }

    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        Paint paint = new Paint();
        paint.setAntiAlias(true);
        paint.setColor(Color.RED);
        paint.setTextSize(40);
        Paint.FontMetrics fm = paint.getFontMetrics();

        Log.d(TAG,"top = " + fm.top);
        Log.d(TAG,"ascent = " + fm.ascent);
        Log.d(TAG,"descent = " + fm.descent);
        Log.d(TAG,"bottom = " + fm.bottom);
        Log.d(TAG,"leading = " + fm.leading);

        int textHeight = (int) (Math.ceil(fm.descent - fm.ascent) + 2);
        Log.d(TAG,"textHeight = " + textHeight);

        float lineWidth = 480.0f;
        float baselineYStartPosition = 200.0f;
        float offsetAscent = baselineYStartPosition + fm.ascent;
        float offsetDescent = baselineYStartPosition + fm.descent;
        float offsetTop = baselineYStartPosition + fm.top;
        float offsetBottom = baselineYStartPosition + fm.bottom;

        canvas.drawText("中文,AaBbPpQqGg", 0, baselineYStartPosition, paint);

        paint.setColor(Color.GREEN);
        canvas.drawLine(0, baselineYStartPosition, lineWidth, baselineYStartPosition, paint);//baseline
        paint.setColor(Color.GRAY);
        canvas.drawLine(0, offsetAscent, lineWidth, offsetAscent, paint);//ascent
        canvas.drawLine(0, offsetDescent, lineWidth, offsetDescent, paint);//descent
        canvas.drawLine(0, offsetTop, lineWidth, offsetTop, paint);//top
        canvas.drawLine(0, offsetBottom, lineWidth, offsetBottom, paint);//bottom
    }
}

    很简单的一个自定义View,运行效果就是上面我们的图片中的效果。在这里我们把baseline和ascent、descent、top以及bottom对应的线都画出来了。所以我们得出一个结论:Canvas.drawText() 的startX是从左下角的baseline的底线开始绘画的,如果我们要得到字体的高度需要关注descent - ascent。
    以后大家再自定义View来绘制文字的时候,一定要注意坐标系的选择,而且如果需要使用到文字高度的时候,千万注意文字的高度是如何计算的,防止算错导致文字绘制出现偏差。

3 个回复

倒序浏览
回复 使用道具 举报
回复 使用道具 举报
多谢分享
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马