Android中canvas中drawText详解

开篇

安卓写自定义View中有一个类相信大家不会陌生,那就是Canvas。Canvas给我们调用者提供的api也很丰富。我们经常用到的画圆(drawCircle),画线(drawLine)。今天我们的要看的问题,是drawText(文字)。为什么要单独说画文字,因为安卓的drawText中,基线问题常常困扰我们,到底该怎么计算基线?正题开始:

1.为什么会有基线?

  • 汉字字母数字不在一个水平线上,g的底部跟h的底部不在同一水平线上(看下图)


    image
  • 在绘制汉字的时候并不是从底部(蓝色线)开始绘制,而是以图中红色的线绘制文字。

2.安卓中如何计算基线

  • 首先,我们都知道我们安卓屏幕的坐标系是右下坐标系,即x轴朝右越来越大,y轴朝下越来越大。知道这个,我们先看下官方提供的api:
/**
     * 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)
    
   
  • 这里要说明的是,这里的drawText中x跟y坐标系都是相对于自定义控件本身矩形框,这边我们主要要关注的就是第三个参数,就是我们今天要讨论的水平基线,从官方解释看,就是在绘制文字时候的沿y轴的基线,在看看第二个参数,官方给出的解释中并没有提基线(baseLine),即是从最初始的位置,即文字最左边开始绘制,这符合我们日常认知。
  • 现在可能对基线还不是很明白,再往下看这张图(来源于其他博客)


  1. 先解释下图中提供的红的边框,来自于Paint(画笔)中getTextBounds方法,即获取文字的矩形区域,这要注意的是,坐标系(重点),获取到的top,left,bottom,rightt都是根据基线跟左边框相交的点为原点的(忽略padding),假设文字宽100px,高20px,同时假设基线距离文字宽的一半位置为3px,则top=-(20/2+3)=-13px,bottom=(20/2-3)=7px,left=0px,right=100px;
    /**
     * 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) {

2.再看图蓝色矩形框,来自于Paint中getFontMetrics方法中,大致意思是获取该字体的相关参数,参照api29文档 大致意思是,ascent 是单行字符距离基线的顶部最打值,top是所有字符距离基线的最高值,descent 是单行字符距离基线的底部最大值,bottom是所有字符距离基线的顶部最大值。

 /**
     * 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;
    }
  1. 我们该利用现有的如何计算基线呢?既然drawText 是根据图中星星位置开始绘制,先算出基线与文字矩形框中线高度的差值(这里是正值),差值+控件高度的一半,即是基线的y坐标。话不多说,上代码
        //先用画笔测量文字
        paint.getTextBounds(text, 0, text.length() - 1, bounds);
        //获取 FontMetrics对象
        Paint.FontMetrics fontMetrics = paint.getFontMetrics();
        // 计算中线跟基线的差值
        float dy = (fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.bottom;
        //计算baseLineY
        float baseLineY = getHeight() / 2 + dy;
    

如有错误,欢迎私聊指正,此博客本人根据B站某位大佬视频中总结的,由于视频是盗版的,贴下大佬的简书地址吧:https://www.jianshu.com/u/35083fcb7747

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,509评论 6 504
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,806评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,875评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,441评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,488评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,365评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,190评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,062评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,500评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,706评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,834评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,559评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,167评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,779评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,912评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,958评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,779评论 2 354