自定义学习⑥drawText()

在学习这边文章之前我对drawText还停留在画出文字,对位置一无所知

概述

1、四线格与基线
小时候,我们在刚开始学习写字母时,用的本子是四线格的,我们必须把字母按照规则写在四线格内。
比如:
自定义学习⑥drawText()_第1张图片
我们在drawText绘制文字的时候也是有基线的,那我们来看一下什么是基线
自定义学习⑥drawText()_第2张图片
可见的基线就是四线格中的第三条线,也就是说基线位置定了,那文字的位置必然就是定了
canvas.drawText()
canvas.drawText()与基线

/**
* text:要绘制的文字
* x:绘制原点x坐标
* y:绘制原点y坐标
* paint:用来做画的画笔
*/
public void drawText(String text, float x, float y, Paint paint)

上面这个构造函数是最常用的drawText方法,传进去一个String对象就能画出对应的文字。
但这里有两个参数需要非常注意,表示原点坐标的x和y.很多同学可能会认为,这里传进去的原点参数(x,y)是所在绘制文字所在矩形的左上角的点。但实际上并不是!比如,我们上面如果要画"harvic’s blog"这几个字,这个原点坐标应当是下图中绿色小点的位置
自定义学习⑥drawText()_第3张图片
在(x,y)中最让人捉急的是y坐标,一般而言,(x,y)所代表的位置是所画图形对应的矩形的左上角点。但在drawText中是非常例外的,y所代表的是基线的位置!


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //自定义基线,然后用drawText画出来
        int baseLineX = 0;
        int baseLineY = 200;
        Paint paint = new Paint();
        paint.setColor(Color.RED);
        canvas.drawLine(baseLineX, baseLineY, 3000, baseLineY, paint);
        //写文字
        paint.setColor(Color.GREEN);
        paint.setTextSize(120);//这便是已px为单位
        //darwText远点定义为0,200
        canvas.drawText("harvic\'s blog", baseLineX, baseLineY, paint);
    }

首先,我们把(0,200)所在的这条横线画出来,所以我先画了一条线从点坐标为(0,200)到点坐标为(3000,200)的一条直线
然后利用canvas.drawText以(0,200)为原点画出文字
效果图:
自定义学习⑥drawText()_第4张图片
其中,红线就是为基线

结论:
1、drawText是中的参数y是基线的位置。
2、一定要清楚的是,只要x坐标、基线位置、文字大小确定以后,文字的位置就是确定的了

paint.setTextAlign(Paint.Align.XXX);

在上面我们讲了,drawText()函数中的Y坐标表示所要绘制文字的基线所在位置。从上面的例子,我们可以看到,我们绘图结果是在X坐标的右边开始绘制的,但这并不是必然的结果。
自定义学习⑥drawText()_第5张图片
我们知道,我们在drawText(text, x, y, paint)中传进去的源点坐标(x,y);其中,y表示的基线的位置。那x代表什么呢?从上面的例子运行结果来看,应当是文字开始绘。
并不是!x代表所要绘制文字所在矩形的相对位置。相对位置就是指指定点(x,y)在在所要绘制矩形的位置。我们知道所绘制矩形的纵坐标是由Y值来确定的,而相对x坐标的位置,只有左、中、右三个位置了。也就是所绘制矩形可能是在x坐标的左侧绘制,也有可能在x坐标的中间,也有可能在x坐标的右侧。而定义在x坐标在所绘制矩形相对位置的函数是:

/**
* 其中Align的取值为:Panit.Align.LEFT,Paint.Align.CENTER,Paint.Align.RIGHT
*/
Paint::setTextAlign(Align align);

我们再来重新看一下上面的例子,当我们设置为不同的值时,绘制结果是怎样的。
同样的代码,我们加上paint.setTextAlign()来设置相对位置来看看结果
(1)、setTextAlign(Paint.Align.LEFT)
在原来代码上加上paint.setTextAlign(Paint.Align.LEFT)

 @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //自定义基线,然后用drawText画出来
        int baseLineX = 0;
        int baseLineY = 200;
        Paint paint = new Paint();
        paint.setColor(Color.RED);
        canvas.drawLine(baseLineX, baseLineY, 3000, baseLineY, paint);
        //写文字
        paint.setColor(Color.GREEN);
        paint.setTextSize(120);//这边是已px为单位
        //darwText远点定义为0,200
        paint.setTextAlign(Paint.Align.LEFT);
        canvas.drawText("harvic\'s blog", baseLineX, baseLineY, paint);
    }

效果图:
自定义学习⑥drawText()_第6张图片
原点(x,y)在矩形的左侧,即矩形从(x,y)的点开始绘制。
(2)、setTextAlign(Paint.Align.CENTER)
效果图:
自定义学习⑥drawText()_第7张图片
(3)、setTextAlign(Paint.Align.RIGHT)
自定义学习⑥drawText()_第8张图片
啥都看不到

二、drawText的四线格与FontMetrics

自定义学习⑥drawText()_第9张图片
除了基线以外,如上图所示,另外还有四条线,分别是ascent,descent,top,bottom,他们的意义分别是:

ascent: 系统建议的,绘制单个字符时,字符应当的最高高度所在线
descent:系统建议的,绘制单个字符时,字符应当的最低高度所在线
top: 可绘制的最高高度所在线
bottom: 可绘制的最低高度所在线
引用一下启舰老师的话,感觉讲的很通俗透彻。
自定义学习⑥drawText()_第10张图片
如上图所示,黑色部分表示电视屏幕,红色框就表示安全区域框。
这个安全框是用来干嘛的?这个安全框就是系统推荐给我们的显示区域,虽然说我们可以讲电视屏幕是每个区域都是可以显示图像的,但由于制式的不同,每个国家的屏幕大小并不一定我们这里的屏幕大小一致,当遇到不一致时,就会裁剪。但系统给我们推荐的显示区域是无论哪种制式都是可以完整显示出来的,所以我们在制作视频时,尽量要把要显示的图像放在所推荐的显示区域内。
同样,在这里,我们在绘制文字时,ascent是推荐的绘制文字的最高高度,就表示在绘制文字时,尽力要在这个最高高度以下绘制文字。descent是推荐的绘制文字的最底高度线,同样表示是在绘制文字时尽量在这个descent线以上来绘制文字。而top线则指该文字可以绘制的最高高度线,bottom则是表示该文字可以绘制的最低高度线。ascent,descent是系统建议上的绘制高度,而top,bottom则是物理上屏幕最高,最低可以画的高度值。他们的差别与我们上面说的视频处理的安全框和屏幕的道理是一样的。
2、FontMetrics
(1)、fontMetrics概述
上面我们讲了,系统在画文字时的五条线,baseline、ascent、descent、top、bottom我们知道baseline的位置是我们在构造drawText()时的参数y来决定的,那ascent,descent,top,bottom这些线的位置要怎么计算出来呢?
Android给我们提供了一个类:FontMetrics,它里面有四个成员变量:


FontMetrics::ascent;
FontMetrics::descent;
FontMetrics::top;
FontMetrics::bottom;

他们的意义与值的计算方法分别如下:

ascent = ascent线的y坐标 - baseline线的y坐标;
descent = descent线的y坐标 - baseline线的y坐标;
top = top线的y坐标 - baseline线的y坐标;
bottom = bottom线的y坐标 - baseline线的y坐标;
我们再来看个图:
自定义学习⑥drawText()_第11张图片
从这个图中,我们先说明两点,然后再回过头来看上面的公式:
1、X轴,Y轴的正方向走向是X轴向右是正方向,Y轴向下是正方向,所以越往下Y坐标越大!
2、大家千万不要将FontMetrics中的ascent,descent,top,bottom与现实中的ascent,descent,top,bottom所在线混淆!这几条线是真实存在的,而FontMetrics中的ascent,descent,top,bottom这个变量的值就是用来计算这几条线的位置的。下面我们将看到如何利用这几个变量来计算这几条线的位置。
看完这个图,我们再重新说说这几个公式,我们拿一个来说吧,其它都是相同的道理。

ascent = ascent线的y坐标 - baseline线的y坐标;//这个公式对我是懵懵懂懂,对xy认识的不够深刻

继续往下看
FontMetrics的这几个变量的值都是以baseline为基准的,对于ascent来说,baseline线在ascent线之下,所以必然baseline的y值要大于ascent线的y值,所以ascent变量的值是负的。(然后看了这个解释就通了,原来就是负的)
同理,对于descent而言:

descent = descent线的y坐标 - baseline线的y坐标;

descent线在baseline线之下,所以必然descent线的y坐标要大于baseline线的y坐标,所以descent变量的值必然是正数。
(2)、得到Text四线格的各线位置
下面,我们就来看看如何通过这些变量来得到对应线所在位置吧。
我们先列出来一个公式:

ascent线Y坐标 = baseline线Y坐标 + fontMetric.ascent;

这就是整个推算过程,没什么难度,同理可以得到:

ascent线Y坐标 = baseline线的y坐标 + fontMetric.ascent;
descent线Y坐标 = baseline线的y坐标 + fontMetric.descent;
top线Y坐标 = baseline线的y坐标 + fontMetric.top;
bottom线Y坐标 = baseline线的y坐标 + fontMetric.bottom;
(3)、获取FontMetrics对象
获取FontMetrics对象是根据paint对象来获取的:

 @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        Paint paint = new Paint();
        Paint.FontMetrics fontMetrics = paint.getFontMetrics();
        Paint.FontMetricsInt fontMetricsInt = paint.getFontMetricsInt();
    }
}

从这里可以看到,通过paint.getFontMetrics()得到对应的FontMetrics对象。这里还有另外一个FontMetrics同样的类叫做FontMetricsInt,它的意义与FontMetrics完全相同,只是得到的值的类型不一样而已,FontMetricsInt中的四个成员变量的值都是Int类型而FontMetrics得到的四个成员变量的值则都是float类型的。很好理解。。
(4)、实例:计算Text四线格位置
我们先写一行字,然后画出这行字中的top线,ascent线,baseline线,descent线和bottom线。

@Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        int baseLineY = 200;
        int baseLineX = 0 ;

        Paint paint = new Paint();
        //写文字
        paint.setColor(Color.GREEN);
        paint.setTextSize(120); //以px为单位
        paint.setTextAlign(Paint.Align.LEFT);
        canvas.drawText("harvic\'s blog", baseLineX, baseLineY, paint);
        //计算各线的位置
        Paint.FontMetrics fontMetrics = paint.getFontMetrics();
        float ascent = baseLineY + fontMetrics.ascent;
        float descent = baseLineY + fontMetrics.descent;
        float top = baseLineY + fontMetrics.top;
        float bottom = baseLineY + fontMetrics.bottom;
        //画基线
        paint.setColor(Color.RED);
        canvas.drawLine(baseLineX, baseLineY, 3000, baseLineY, paint);

        //画top
        paint.setColor(Color.BLUE);
        canvas.drawLine(baseLineX, top, 3000, top, paint);

        //画ascent
        paint.setColor(Color.GREEN);
        canvas.drawLine(baseLineX, ascent, 3000, ascent, paint);

        //画descent
        paint.setColor(Color.YELLOW);
        canvas.drawLine(baseLineX, descent, 3000, descent, paint);

        //画bottom
        paint.setColor(Color.RED);
        canvas.drawLine(baseLineX, bottom, 3000, bottom, paint);

    }

很好理解,总共分为三部分,写文字、计算各线所在位置、画出各条线
效果图:
自定义学习⑥drawText()_第12张图片

三、所绘文字宽度、高度和最小矩形获取

先看一下要实现的效果图
自定义学习⑥drawText()_第13张图片
在这张图中,文字底部的绿色框就是所绘制字符串所占据的大小。我们要求的宽度和高度也就是这个绿色框的宽度和高度。
从图中也可以看到,红色框部分,它的宽和高紧紧包围着字符串,所以红色框就是我们要求的最小矩形。即能包裹字符串的最小矩形。

1、字符串所占高度和宽度

(1)、高度

字符串所占高度很容易得到,直接用bottom线所在位置的Y坐标减去top线所在位置的Y坐标就是字符串所占的高度:
代码如下:

Paint.FontMetricsInt fm = paint.getFontMetricsInt();
int top = baseLineY + fm.top;
int bottom = baseLineY + fm.bottom;
//所占高度
int height = bottom - top;

(2)、宽度
宽度是非常容易得到的,直接利用下面的函数就可以得到

int width = paint.measureText(String text);

使用示例如下:

Paint paint = new Paint();
 
//设置paint
paint.setTextSize(120); //以px为单位
//获取宽度
int width = (int)paint.measureText("harvic\'s blog");

(3)、最小矩形
1、概述
要获取最小矩形,也是通过系统函数来获取的,函数及意义如下:

/**
 * 获取指定字符串所对应的最小矩形,以(0,0)点所在位置为基线
 * @param text  要测量最小矩形的字符串
 * @param start 要测量起始字符在字符串中的索引
 * @param end   所要测量的字符的长度
 * @param bounds 接收测量结果
 */
public void getTextBounds(String text, int start, int end, Rect bounds);

学习文章自定义控件之绘图篇( 五):drawText()详解

你可能感兴趣的:(Android)