android学习16#--学习canvas.drawText

本来这篇文章是要写写我在设计高级跑马灯程序的心得的,但是编写过程中花了近一天多的时间搞明白canvas.drawText中的第三个参数[float y]代表的真实含义。学习本文应该能帮助大家掌握FontMetrics类和Rect类成员变量值具体含义。

drawText引出问题

先来看看api中是如何定义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) {
        native_drawText(mNativeCanvasWrapper, text, 0, text.length(), x, y, paint.mBidiFlags,
                paint.getNativeInstance(), paint.mNativeTypeface);
    }

简单解释下各参数:

@param text:要显示的文本内容,这个不难理解。

@param x:文本相对屏幕原点x方向距离,没有更深的含义,也比较好理解

@param y:文本的baseline相对屏幕原点y方向距离。好,问题来了,baseline是个什么鬼东东

@param paint:画笔

baseline的来龙去脉

先看看baseline在文字区域的位置。通过一个实例来分析,这里只贴主要的code。

    @Override
    protected void onDraw(Canvas canvas) {
        //super.onDraw(canvas);

        mRect = new Rect();
        mPaint = new Paint();
        mPaint.setTextSize(64);
        mPaint.setColor(Color.RED);

        Paint.FontMetricsInt fontMetrics = mPaint.getFontMetricsInt();
        mPaint.getTextBounds(textStr, 0, textStr.length(), mRect);

        int w = mRect.width();
        int h = mRect.height();

        mRect = new Rect(mRectLeft, mRectTop, mRectLeft+w, mRectTop+(-fontMetrics.top)+fontMetrics.bottom);

        canvas.drawRect(mRect, mPaint);

        mBaseLine = (mRect.bottom+mRect.top)/2
                - (fontMetrics.bottom+fontMetrics.top)/2;

        mBaseLine = -fontMetrics.top;

        /*其中y == mBaseLine的值*/
        mPaint.setColor(Color.WHITE);
        canvas.drawText(textStr, mRectLeft, mBaseLine, mPaint);

        mPaint.setColor(Color.BLUE);
        mPaint.setStrokeWidth(3);
        /*将mBaseLine所在的位置画出来*/
        canvas.drawLine(mRectLeft, mBaseLine, mRectLeft+w, mBaseLine, mPaint);
    }

效果图:

分析

从上图可以看出来,baseline并不是文字区域的底部,但是这根线的位置相信大家都熟悉,除非你没有学过英语。

FontMetrics

FontMetrics是Paint静态内部类,主要定义了Paint绘制文本时的关键坐标。API中这么描述:

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(). 

主要意思:根据设定的fontsize来获得字体的各种变量值。通过getFontMetrics()方法来获取。

FontMetrics类含有5个成员变量:

    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;
    }

很明显所有的值都是基于baseline来算的。这里我主要关注top这个值的定义。

top:api的注释翻译过来意思是字体可绘制区域最高位置与baseline的距离。但是它是个负数,所以-top就是我们的baseline坐标值。

ascent:字体下单个字符最高位置与baseline的距离

descent:字体下单个字符最低位置与baseline的距离,与ascent镜像对应

bottom:字体可绘制区域最低位置与baseline的距离。

leading:字面翻译是文本线额外的空间。一般都为0,不知道有何用处。

Rect

Rect类表示的一块矩形区域。Paint类中getTextBounds()方法可以获取对应font size下文本所占用矩形区域。记住这个矩形区域的坐标值也是相对baseline来计算的。

Rect类成员变量有4个,分别是left、top、right、bottom。由于文本的rect区域是相对baseline来计算的,因此真实的top跟bottom都要加上baseline的值。

借助一个实例来理解FontMetrics和Rect

主要源码:

    @Override
    protected void onDraw(Canvas canvas) {
        //super.onDraw(canvas);

        mRect = new Rect();
        mPaint = new Paint();
        mPaint.getTextBounds(textStr, 0, 1, mRect);
        mPaint.setTextSize(textSize);
        mPaint.setColor(0xffF4A460);

        Paint.FontMetricsInt fontMetrics = mPaint.getFontMetricsInt();
        mPaint.getTextBounds(textStr, 0, textStr.length(), mRect);

        int w = mRect.width();
        int h = mRect.height();

        /*给fontMetrics区域填充0xffF4A460色*/
        mRect = new Rect(mRectLeft, mRectTop, mRectLeft+w, mRectTop+(-fontMetrics.top)+fontMetrics.bottom);
        canvas.drawRect(mRect, mPaint);

        /*计算baseline,其中mRectTop是控件顶部到父view的距离*/
        mBaseLine = -fontMetrics.top + mRectTop;

        /*其中y == mBaseLine的值*/
        mPaint.setColor(Color.WHITE);
        canvas.drawText(textStr, mRectLeft, mBaseLine, mPaint);

        mPaint.setColor(Color.BLUE);
        mPaint.setStrokeWidth(4);
        /*蓝色线:将mBaseLine所在的位置画出来*/
        canvas.drawLine(mRectLeft, mBaseLine, mRectLeft+w, mBaseLine, mPaint);

        mPaint.setColor(Color.GREEN);
        /*绿色线:将文字区域的bottom所在的位置画出来*/
        canvas.drawLine(mRectLeft, mBaseLine+fontMetrics.bottom, mRectLeft+2*w
                , mBaseLine+fontMetrics.bottom, mPaint);

        mPaint.setColor(Color.RED);
        /*红色线:将文字区域的top所在的位置画出来*/
        canvas.drawLine(mRectLeft, mBaseLine+fontMetrics.top, mRectLeft+2*w
                , mBaseLine+fontMetrics.top, mPaint);

        mPaint.setColor(Color.GRAY);
        /*灰色线:将文字区域的ascent所在的位置画出来*/
        canvas.drawLine(mRectLeft, mBaseLine+fontMetrics.ascent, mRectLeft+w+50
                , mBaseLine+fontMetrics.ascent, mPaint);

        mPaint.setColor(Color.WHITE);
        /*白色线:将文字区域的descent所在的位置画出来*/
        canvas.drawLine(mRectLeft, mBaseLine+fontMetrics.descent, mRectLeft+w+50
                , mBaseLine+fontMetrics.descent, mPaint);

        mPaint.setColor(Color.YELLOW);
        /*黄色线:将文字区域的leading所在的位置画出来*/
        canvas.drawLine(mRectLeft, mBaseLine+fontMetrics.leading, mRectLeft+w/2
                , mBaseLine+fontMetrics.leading, mPaint);

        /*将“测”字的rect用矩形绘出来*/
        mPaint.setColor(Color.BLACK);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.getTextBounds(textStr, 0, 1, mRect);
        mRect.top+=mBaseLine;
        mRect.bottom+=mBaseLine;
        canvas.drawRect(mRect, mPaint);

        /*将“测试:12hg”字的rect用矩形绘出来*/
        mPaint.setColor(Color.BLACK);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.getTextBounds(textStr, 0, 7, mRect);
        mRect.top+=mBaseLine;
        mRect.bottom+=mBaseLine;
        canvas.drawRect(mRect, mPaint);
    }

效果图:

FontMetrics:

1. 浅黄色区域就是文字的FontMetrics区域,可以看出来文字实在此区域垂直居中绘制

2. 红色线:为FontMetrics的top

3. 灰色线:为FontMetrics的ascent

4. 黄色线:为FontMetrics的leading

5. 蓝色线:为baseline线

6. 白色线:为FontMetrics的descent

7. 绿色线:为FontMetrics的bottom

Rect:

1. 第一个小黑矩形为“测”字的rect区域

2. 第二个长黑矩形为“测试:12hg”字的rect区域

总结

只要彻底弄明白baseline才能轻松掌握drawText接口,才不至于开发的时候总是搞不明白绘制的文字要么偏上要么偏下。

参考

http://blog.csdn.net/hursing/article/details/18703599

http://892848153.iteye.com/blog/2205002

http://blog.csdn.net/linghu_java/article/details/46404081

http://blog.csdn.net/sirnuo/article/details/21165665

http://gundumw100.iteye.com/blog/1025185

时间: 2024-11-04 15:19:10

android学习16#--学习canvas.drawText的相关文章

Android使用学习之画图(Canvas,Paint)与手势感应及其应用(乒乓球小游戏)

作为一个没有学习Android的菜鸟,近期一直在工作之外努力地学习的Android的使用. 这周看了下Android的画图.主要是Canvas,Paint等,感觉须要实践下.下午正好有空,就想整一个乒乓球的游戏,算是巩固学的知识. 首先,须要了解下Android的画图须要掌握的经常使用类.包含Canvas,就像一个画板一样,全部的东西都是在其上画的.Paint就是画笔.用其能够画各种基本图形和文字.       Canvas和Paint经常使用的方法就不列举了,这种东西网上到处是.有了这两个东西

Android 2D Graphics学习 Region和Canvas裁剪

1.首先介绍Region类 Region,中文意思即区域的意思,它表示的是canvas图层上的某一块封闭的区域. [java] view plaincopyprint? /**构造方法*/ public Region()  //创建一个空的区域 public Region(Region region) //拷贝一个region的范围 public Region(Rect r)  //创建一个矩形的区域 public Region(int left, int top, int right, int

Android之SurfaceView学习(一)转转

Android之SurfaceView学习(一) 首先我们先来看下官方API对SurfaceView的介绍 SurfaceView的API介绍 Provides a dedicated drawing surface embedded inside of a view hierarchy. You can control the format of this surface and, if you like, its size; the SurfaceView takes care of pla

Android自定义view学习笔记02

Android自定义view学习笔记02 本文代码来自于张鸿洋老师的博客之Android 自定义View (二) 进阶 学习笔记,对代码进行些许修改,并补充一些在coding过程中遇到的问题.学习的新东西. 相关代码 //CustomImageView.java package mmrx.com.myuserdefinedview.textview; import android.content.Context; import android.content.res.TypedArray; im

Android自定义View学习笔记04

Android自定义View学习笔记04 好长时间没有写相关的博客了,前几周在帮学姐做毕设,所以博客方面有些耽误.过程中写了一个类似wp的磁贴的view,想再写个配套的layout,所以昨天看了一下自定义viewGroup的相关知识-晚上睡觉想了一下可行性不是很高-代码量还不如直接自己在xml上写来得快,速度上也是个问题.今天看了一下张鸿洋老师的Android 自定义View (三) 圆环交替 等待效果这篇博文,再加上前一段时间看到的一幅图,结合之前写的一个圆形imageView的实现博文And

Android 自定义View学习(2)

上一篇学习了基本用法,今天学一下稍微复杂一点的,先看一下效果图 为了完成上面的效果还是要用到上一期开头的四步 1,属性应该要有颜色,要有速度 <?xml version="1.0" encoding="utf-8"?> <resources> <attr name="speed" format="integer" /> <attr name="circleColor"

Android Web Service学习总结(一)

最近学习android平台调用webWebService,学习了一篇不错的博客(http://blog.csdn.net/lyq8479/article/details/6428288),可惜是2011年时的方法,而不适合现在android4.0之后的android版本,所以通过一番学习和研究,总结如下. web Service简介 通俗的理解:通过使用WebService,我们能够像调用本地方法一样去调用远程服务器上的方法.我们并不需要关心远程的那个方法是Java写的,还是PHP或C#写的:我

Android ARM指令学习

在逆向分析Android APK的时候,往往需要分析它的.so文件.这个.so文件就是Linux的动态链接库,只不过是在ARM-cpu下编译的.所以学习Android下的ARM指令很重要.目前,市面上的ARM-cpu基本都支持一种叫做THUMB的指令集模式.这个THUMB指令集可以看作是ARM指令集的子集,只不过ARM指令集为32bit,THUMB指令集为16bit.之所以要使用这个THUMB指令集,主要是为了提升代码密度.具体信息大家可以google. 下面介绍如何简单修改.so文件. 首先,

Android Web Service学习总结(二)

上篇文章做好了准备工作,现在就实践(android平台调用web service实现号码归属地查询) 1.    Ksoap2-android简介 在Android平台调用web service需要依赖于第三方类库ksoap2,它是一个SOAP Web service客户端开发包,主要用于资源受限制的Java环境如Applets或J2ME应用程序(CLDC/ CDC/MIDP).在Android平台中我们并不会直接使用ksoap2,而是使用ksoap2 android.KSoap2 Androi