android自定义view DrawText()方法文字竖向居中解决方案。

问题:

自定义view的时候,drawText()方法画出的文字竖直方向没有居中,如下代码:

 @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //y轴
        canvas.drawLine(getWidth() / 2, 0, getWidth() / 2, getHeight(), paint);
        //x轴
        canvas.drawLine(0, getHeight() / 2, getWidth(), getHeight() / 2, paint);
        canvas.drawText("helloWorld", getWidth() / 2, getHeight() / 2, paint);
    }

在自定义view中画了x轴和y轴方便看清位置。文字“helloWorld”的位置为(getWidth() / 2, getHeight() / 2),然而得出的效果是这样的:

在这里插入图片描述

可见文字并没有位于控件的正中间,而是向上偏移了一点;

分析:

查看drawText()的源码:

 /**
     * Draw the text, with origin at (x,y), using the specified paint. The origin is interpreted
     * based on the Align setting in the paint.
     *
     * @param text The text to be drawn
     * @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
     * @param paint The paint used for the text (e.g. color, size, style)
     */
    public void drawText(@NonNull String text, float x, float y, @NonNull Paint paint) {
        super.drawText(text, x, y, paint);
    }

从上面的代码看出参数y代表的是文字的“baseline”位置,所以说android中drawText是以文字的基线为标准的,我们要文字“居中”的话就必须把偏移量计算出来。这就需要用到一个非常关键的类: Paint.FontMetrics
FontMetrics
其中主要参数代表含义如下:
在这里插入图片描述

  1. 基准点是baseline,值为0

  2. Ascent是baseline之上至字符最高处的距离,值为负数

  3. Descent是baseline之下至字符最低处的距离,值为正数

  4. Leading文档说的很含糊,其实是上一行字符的descent到下一行的ascent之间的距离

  5. Top指的是指的是最高字符到baseline的值,即ascent的最大值,为负数

  6. 同上,bottom指的是最下字符到baseline的值,即descent的最大值,为正数;

那么文字中间的值为 ( top+bottom) / 2,因为以baseline为基线(0),所以偏移量:
offset = 0 - ( top+bottom) / 2;
讲上述代码改为:

@Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //y轴
        canvas.drawLine(getWidth() / 2, 0, getWidth() / 2, getHeight(), paint);
        //x轴
        canvas.drawLine(0, getHeight() / 2, getWidth(), getHeight() / 2, paint);
        String text="aaaa";
        fontMetrics = paint.getFontMetrics();
        float top=fontMetrics.top;
        float bottom=fontMetrics.bottom;
        float offset=(top+bottom)/2;
        canvas.drawText(text, getWidth() / 2, getHeight() / 2-offset, paint);
    }

效果如下:
在这里插入图片描述
文字高度居中了,但是又发现新的问题:上述算法是计算的是字符占据的最大高度,如“h”和“e”两者的top值是一样的,如果全部字符都比较“矮”会出现什么情况呢?

将“helloWorld”改为“aaaaa”:
在这里插入图片描述
其实这样没有错,因为“a”、“s”、“u”、“r”、“w”这些字符本身比较矮,上部分留白了所以看起来没有居中。但是如果想让这些“矮子”也居中怎么办呢? android中提供了 getTextBound()方法:

/**
     * Retrieve the text boundary box and store to bounds.
     *
     * Return in bounds (allocated by the caller) the smallest rectangle that
     * encloses all of the characters, with an implied origin at (0,0).
     *
     * @param text string to measure and return its bounds
     * @param start index of the first char in the string to measure
     * @param end 1 past the last char in the string to measure
     * @param bounds returns the unioned bounds of all the text. Must be allocated by the caller
     */
    public void getTextBounds(String text, int start, int end, Rect bounds) {
        if ((start | end | (end - start) | (text.length() - end)) < 0) {
            throw new IndexOutOfBoundsException();
        }
        if (bounds == null) {
            throw new NullPointerException("need bounds Rect");
        }
        nGetStringBounds(mNativePaint, text, start, end, mBidiFlags, bounds);
    }

意思返回绘制文字边界的最小矩形,也就是说如果文字全是a、u等矮字符那么它返回的高度就是“a”字符的高度,不会像上述 fontMetrics.top 方法加上留白的距离;
修改代码:

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //y轴
        canvas.drawLine(getWidth() / 2, 0, getWidth() / 2, getHeight(), paint);
        //x轴
        canvas.drawLine(0, getHeight() / 2, getWidth(), getHeight() / 2, paint);
        String text="aaaaa";
        rect = new Rect();
        paint.getTextBounds(text,0,text.length(),rect);
        float offset=(rect.top+rect.bottom)/2;
        canvas.drawText(text, getWidth() / 2, getHeight() / 2-offset, paint);
    }

效果:
在这里插入图片描述

总结:

1 . 常用方法:

   float offset=(paint.getFontMetrics().top+paint.getFontMetrics().bottom)/2;
   canvas.drawText(text, x, y-offset, paint);

此方法如果全部文字都比较矮如 “aaaa” ,文字上方会留白。

2 . 让矮文字也居中的方法:

 rect = new Rect();
 paint.getTextBounds(text,0,text.length(),rect);
 float offset=(rect.top+rect.bottom)/2;
 canvas.drawText(text,x, y-offset, paint);

如果是频繁变动的文字,当文字变成都是矮文字时会因为文字上移出现视觉上的跳动(看需求选择吧=-=)。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值