自定义控件之绘图篇:drawText详解_drawtext 和 drawtextlayout-程序员宅基地

一、概述

1. 四线格与基线

小时候,我们在刚开始学习写字母时,用的本子是四线格的,我们必须把字母按照规则写在四线格内。

比如:

自定义控件之绘图篇:drawText详解

那么问题来了,在canvas在利用drawText绘制文字时,也是有规则的,这个规则就是baseline(基线)!

我们先来看一下什么是基线:

自定义控件之绘图篇:drawText详解

对比以上两图,可见基线就是四线格中的第三条线!也就是说,只要基线的位置定了,那文字的位置必然是定了的!

2. canvas.drawText()

(1)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)是所在绘制文字所在矩形的左上角的点。但实际上并不是!比如,我们上面如果要画“matt’s blog”这几个字,这个原点坐标应当是下图中绿色小点的位置

自定义控件之绘图篇:drawText详解

(2)实例

下面我们就举个例子来看一下drawText中,原点坐标(x,y)的位置。

  • 新建一个工程,创建一个MainActivity,然后创建一个CustomTextView继承View

    重写onDraw函数

//重写onDraw函数,自定义一个基线,然后利用drawText画出来

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

int baseLineX = 0 ;

int baseLineY = 200;

//画基线

Paint paint = new Paint();

paint.setColor(Color.RED);

canvas.drawLine(baseLineX, baseLineY, 2500, baseLineY, paint);

//画文字

paint.setColor(Color.BLUE);

paint.setTextSize(120); //以px为单位

canvas.drawText("matt's blog", baseLineX, baseLineY, paint);

}

在这里,先定义drawText原点的位置:(0,200)

首先,我们把(0,200)所在的这条横线画出来,所以我先画了一条线从点坐标为(0,200)到点坐标为(2500,200)的一条直线

然后利用canvas.drawText以(0,200)为原点画出文字

  • 在main.xml添加自定义view的引用

<?xml version="1.0" encoding="utf-8"?>

<android.support.constraint.ConstraintLayout

xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:tools="http://schemas.android.com/tools"

xmlns:app="http://schemas.android.com/apk/res-auto"

android:layout_width="match_parent"

android:layout_height="match_parent"

app:layout_behavior="@string/appbar_scrolling_view_behavior"

tools:showIn="@layout/app_bar_main"

tools:context="cn.addapp.customviews.MainActivity">

<cn.addapp.customviews.views.CustomTextView

android:layout_width="match_parent"

android:layout_height="match_parent"/>

</android.support.constraint.ConstraintLayout>

效果图如下:

自定义控件之绘图篇:drawText详解

结论:

1. drawText是中的参数y是基线的位置。

2. 一定要清楚的是,只要x坐标、基线位置、文字大小确定以后,文字的位置就是确定的了。

3. paint.setTextAlign(Paint.Align.XXX)

在上面我们讲了,drawText()函数中的Y坐标表示所要绘制文字的基线所在位置。从上面的例子,我们可以看到,我们绘图结果是在X坐标的右边开始绘制的,但这并不是必然的结果。

我们来看一张图:

自定义控件之绘图篇:drawText详解

我们知道,我们在drawText(text, x, y, paint)中传进去的源点坐标(x,y);其中,y表示的基线的位置。那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)

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

int baseLineY = 200;

int baseLineX = 0 ;

//画基线

Paint paint = new Paint();

paint.setColor(Color.RED);

canvas.drawLine(baseLineX, baseLineY, 2500, baseLineY, paint);

//画文字

paint.setColor(Color.BLUE);

paint.setTextSize(120); //以px为单位

paint.setTextAlign(Paint.Align.LEFT);

canvas.drawText("matt's blog", baseLineX, baseLineY, paint);

}

运行结果如下:

自定义控件之绘图篇:drawText详解

可见,原点(x,y)在矩形的左侧,即矩形从(x,y)的点开始绘制

(2)setTextAlign(Paint.Align.CENTER)

同样的代码,把相对位置设置为:setTextAlign(Paint.Align.CENTER)

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

int baseLineY = 200;

int baseLineX = 0 ;

//画基线

Paint paint = new Paint();

paint.setColor(Color.RED);

canvas.drawLine(baseLineX, baseLineY, 2500, baseLineY, paint);

//画文字

paint.setColor(Color.BLUE);

paint.setTextSize(120); //以px为单位

paint.setTextAlign(Paint.Align.CENTER);

canvas.drawText("matt's blog", baseLineX, baseLineY, paint);

}

运行结果如下:

自定义控件之绘图篇:drawText详解

所以原点(x,y)就在所要绘制文字所在矩形区域的正中间,换句话说,系统会根据(x,y)的位置和文字所在矩形大小,会计算出当前开始绘制的点。以使原点(x,y)正好在所要绘制的矩形的正中间。

(3)setTextAlign(Paint.Align.RIGHT)

同样的代码,把相对位置设置为:setTextAlign(Paint.Align.RIGHT)

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

int baseLineY = 200;

int baseLineX = 0 ;

//画基线

Paint paint = new Paint();

paint.setColor(Color.RED);

canvas.drawLine(baseLineX, baseLineY, 2500, baseLineY, paint);

//画文字

paint.setColor(Color.BLUE);

paint.setTextSize(120); //以px为单位

paint.setTextAlign(Paint.Align.RIGHT);

canvas.drawText("matt's blog", baseLineX, baseLineY, paint);

}

运行结果如下:

自定义控件之绘图篇:drawText详解

所以原点(x,y)应当在所要绘制矩形的右侧,在上面的代码中,也就是说整个矩形都在(0,200)的左侧,所以我们看到的是什么都没有。

(4)注意

下面,我们再看一个例子:

我们只写一个大写字母 M,然后将其相对位置设置为paint.setTextAlign(Paint.Align.CENTER)

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

int baseLineY = 200;

int baseLineX = 0 ;

//画基线

Paint paint = new Paint();

paint.setColor(Color.RED);

canvas.drawLine(baseLineX, baseLineY, 2500, baseLineY, paint);

//画文字

paint.setColor(Color.BLUE);

paint.setTextSize(120); //以px为单位

paint.setTextAlign(Paint.Align.CENTER);

canvas.drawText("M", baseLineX, baseLineY, paint);

}

运行结果如下:

自定义控件之绘图篇:drawText详解

我们可以看到字母M在原点(0,200)的正中间。

这也就是我们要强调的:相对位置是根据所要绘制文字所在矩形来计算的。

二、drawText的四线格与FontMetrics

1、Text的绘图四线格

前面我们讲了基线,其实除了基线,系统在绘制Text时,还是有其它线的,我们来看个图:

自定义控件之绘图篇:drawText详解

除了基线以外,如上图所示,另外还有四条线,分别是ascent,descent,top,bottom,他们的意义分别是:

  • ascent: 系统建议的,绘制单个字符时,字符应当的最高高度所在线

  • descent:系统建议的,绘制单个字符时,字符应当的最低高度所在线

  • top: 可绘制的最高高度所在线

  • bottom: 可绘制的最低高度所在线

单从这几个定义,大家可能还是搞不清这几值到底是什么意义。我们来看一下电视的显示。用过视频处理工具的同学(比如premiere,AE,绘声绘影等),应该都会知道,在制作视频时,视频显示位置都会有一个安全区域框,如下图所示:

自定义控件之绘图篇:drawText详解

如上图所示,黑色部分表示电视屏幕,红色框就表示安全区域框。

这个安全框是用来干嘛的?这个安全框就是系统推荐给我们的显示区域,虽然说我们可以讲电视屏幕是每个区域都是可以显示图像的,但由于制式的不同,每个国家的屏幕大小并不一定我们这里的屏幕大小一致,当遇到不一致时,就会裁剪。但系统给我们推荐的显示区域是无论哪种制式都是可以完整显示出来的,所以我们在制作视频时,尽量要把要显示的图像放在所推荐的显示区域内。

同样,在这里,我们在绘制文字时,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详解

从这个图中,我们先说明两点,然后再回过头来看上面的公式:

1、X轴,Y轴的正方向走向是X轴向右是正方向,Y轴向下是正方向,所以越往右X坐标越大,越往下Y坐标越大!

2、大家千万不要将FontMetrics中的ascent,descent,top,bottom与现实中的ascent,descent,top,bottom所在线混淆!

这几条线是真实存在的,而FontMetrics中的ascent,descent,top,bottom这个变量的值就是用来计算这几条线的位置的。下面我们将看到如何利用这几个变量来计算这几条线的位置。

看完这个图,我们再重新说说这几个公式,我们拿一个来说吧,其它都是相同的道理。

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

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坐标减去从baseline线到ascent线的这段距离。

也就是:(|fontMetric.ascent|表示取绝对值)

ascent线Y坐标 = baseline线Y坐标 - |fontMetric.ascent|;

又因为fontMetric.ascent是负值,所以:

ascent线Y坐标 = baseline线Y坐标 - |fontMetric.ascent|;

ascent线Y坐标 = baseline线Y坐标 - ( -fontMetric.ascent);

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对象来获取的:

Paint paint = new Paint();

Paint.FontMetrics fm = paint.getFontMetrics();

Paint.FontMetricsInt fmInt = paint.getFontMetricsInt();

从这里可以看到,通过paint.getFontMetrics()得到对应的FontMetrics对象。这里还有另外一个FontMetrics同样的类叫做FontMetricsInt,它的意义与FontMetrics完全相同,只是得到的值的类型不一样而已,FontMetricsInt中的四个成员变量的值都是Int类型,而FontMetrics得到的四个成员变量的值则都是float类型的。

(4)、实例:计算Text四线格位置

自定义控件之绘图篇:drawText详解

在这个例子中,我们先写一行字,然后画出这行字中的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.BLUE);

paint.setTextSize(120); //以px为单位

paint.setTextAlign(Paint.Align.LEFT);

canvas.drawText("matt's blog", baseLineX, baseLineY, paint);

//计算各线在位置

Paint.FontMetrics fm = paint.getFontMetrics();

float ascent = baseLineY + fm.ascent;

float descent = baseLineY + fm.descent;

float top = baseLineY + fm.top;

float bottom = baseLineY + fm.bottom;

//画基线

paint.setColor(Color.parseColor("#FF0000"));

paint.setFakeBoldText(true);

paint.setStrokeWidth(3);

canvas.drawLine(baseLineX, baseLineY, 2500, baseLineY, paint);

//画top

paint.setColor(Color.parseColor("#196F3D"));

canvas.drawLine(baseLineX, top, 2500, top, paint);

//画ascent

paint.setColor(Color.parseColor("#1A5276"));

canvas.drawLine(baseLineX, ascent, 2500, ascent, paint);

//画descent

paint.setColor(Color.parseColor("#F1C40F"));

canvas.drawLine(baseLineX, descent, 2500, descent, paint);

//画bottom

paint.setColor(Color.parseColor("#33FF00"));

canvas.drawLine(baseLineX, bottom, 2500, bottom, paint);

}

这段代码中,总共分为三部分,写文字、计算各线所在位置、画出各条线;我们逐段来讲

先看写文字:

int baseLineY = 200;

int baseLineX = 0 ;

Paint paint = new Paint();

//写文字

paint.setColor(Color.BLUE);

paint.setTextSize(120); //以px为单位

paint.setTextAlign(Paint.Align.LEFT);

canvas.drawText("matt's blog", baseLineX, baseLineY, paint);

有关drawText的问题我们已经讲过,在这段代码中,我们需要注意的是两点:

1、drawText中的参数y是基线的位置

2、paint.setTextAlign(Paint.Align.LEFT);设置的相对位置为,指定的原点(0,200)在绘制矩形的左侧。换句话说,所绘制的文字所在矩形在(0,200)点的右侧

然后是计算各各线的y坐标位置:

//计算各线在位置

Paint.FontMetrics fm = paint.getFontMetrics();

float ascent = baseLineY + fm.ascent;

float descent = baseLineY + fm.descent;

float top = baseLineY + fm.top;

float bottom = baseLineY + fm.bottom;

首先,利用 paint.getFontMetrics()得到FontMetrics的实例,然后利用我们上面的公式即可得到各条线的y坐标。

最后就是利用这些y坐标画出这些线了,很简单,就是drawLine的使用,难度不大,就不再细讲。

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

这部分,我们将讲解如何获取所绘制字符串所占区域的高度、宽度和仅包裹字符串的最小矩形。我们来看张图来讲述下他们的意义:

自定义控件之绘图篇:drawText详解

在这张图中,文字底部的绿色框就是所绘制字符串所占据的大小。我们要求的宽度和高度也就是这个绿色框的宽度和高度。

从图中也可以看到,红色框部分,它的宽和高紧紧包围着字符串,所以红色框就是我们要求的最小矩形。即能包裹字符串的最小矩形。

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

使用示例如下:

String text = "matt's blog";

Paint paint = new Paint();

//设置paint

paint.setTextSize(120); //以px为单位

//获取宽度

int width = (int)paint.measureText(text);

(3)、最小矩形

1、概述

要获取最小矩形,也是通过系统函数来获取的,函数及意义如下:

/**

* 获取指定字符串所对应的最小矩形,以(0,0)点所在位置为基线

* @param text 要测量最小矩形的字符串

* @param start 要测量起始字符在字符串中的索引

* @param end 所要测量的字符的长度

* @param bounds 接收测量结果

*/

public void getTextBounds(String text, int start, int end, Rect bounds);

我们简单展示下使用代码及结果:

String text = "matt's blog";

Paint paint = new Paint();

//设置paint

paint.setTextSize(120); //以px为单位

Rect rect = new Rect();

paint.getTextBounds(text,0,text.length(),rect);

Log.e("matt",rect.toShortString());

在这段代码中,首先设置字体大小,然后利用paint.getTextBounds()得到最小矩形,最后,我将其打印出来

结果如下:

自定义控件之绘图篇:drawText详解

可以看到这个矩形的左上角位置为(8,-90),右下角的位置为(580,25);

大家可能会有疑问,为什么左上角的Y坐标是个负数?从代码中,我们也可以看到,我们并没有给getTextBounds()传递基线位置。那它就是以(0,0)为基线来得到这个最小矩形的!所以这个最小矩形的位置就是以(0,0)为基线的结果!

2、得到最小矩形的实际位置

我们先来看一个原理:

自定义控件之绘图篇:drawText详解

在上面这个图中,我们将黑色矩形平行下移距离Y(黄色线依照的是基线的位置),那么平移后的左上角点的y坐标就是 y2 = y1 + Y;

同样的道理,由于paint.getTextBounds()得到最小矩形的基线是y = 0;那我们直接将这个矩形移动baseline的距离就可以得到这个矩形实际应当在的位置了。

所以矩形应当所在实际位置的坐标是:

Rect minRect = new Rect();

paint.getTextBounds(text,0,text.length(),minRect);

//最小矩形,实际top位置

int minTop = minRect.top + baselineY;

//最小矩形,实际bottom位置

int minBottom = minRect.bottom + baselineY;

3、实例

下面我们就举个例子来看一下我们列举的这几个函数的使用方法

效果图与这一节开篇时的效果图是一样的,如下:

自定义控件之绘图篇:drawText详解

我们先看一下完整的代码,然后再细讲

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

String text = "matt's blog";

int baseLineY = 200;

int baseLineX = 0 ;

//设置paint

Paint paint = new Paint();

paint.setTextSize(120); //以px为单位

paint.setTextAlign(Paint.Align.LEFT);

//画text所占的区域

Paint.FontMetricsInt fm = paint.getFontMetricsInt();

int top = baseLineY + fm.top;

int bottom = baseLineY + fm.bottom;

int width = (int)paint.measureText(text);

Rect rect = new Rect(baseLineX,top,baseLineX+width,bottom);

paint.setColor(Color.GREEN);

canvas.drawRect(rect,paint);

//画最小矩形

Rect minRect = new Rect();

paint.getTextBounds(text,0,text.length(),minRect);

minRect.top = baseLineY + minRect.top;

minRect.bottom = baseLineY + minRect.bottom;

paint.setColor(Color.RED);

canvas.drawRect(minRect,paint);

//写文字

paint.setColor(Color.WHITE);

canvas.drawText(text, baseLineX, baseLineY, paint);

}

这段代码总共分为四部分:设置paint,画字符串所占据矩形,画最小矩形,画文字

第一部分:设置paint

String text = "matt's blog";

int baseLineY = 200;

int baseLineX = 0 ;

//设置paint

Paint paint = new Paint();

paint.setTextSize(120); //以px为单位

paint.setTextAlign(Paint.Align.LEFT);

设置paint这部分,主要是设置字体的大小,因为我们在字体所占的区域大小跟字体的大小是有直接关系的,如果不设置,那么在获取所占区域大小时,将利用系统默认的大小来测量了,当然是不行的。

第二部分:画text所占的区域

//画text所占的区域

Paint.FontMetricsInt fm = paint.getFontMetricsInt();

int top = baseLineY + fm.top;

int bottom = baseLineY + fm.bottom;

int width = (int)paint.measureText(text);

Rect rect = new Rect(baseLineX,top,baseLineX+width,bottom);

paint.setColor(Color.GREEN);

canvas.drawRect(rect,paint);

这里就是利用我们前面我们讲过的获取top线和bottom线的方法,获取宽度时就是利用paint.measureText(text);

然后利用求得到top,bottom,width来得到对应的矩形:Rect(baseLineX,top,baseLineX+width,bottom),这里要注意的是我们利用paint.measureText(text)得到的只是宽度,矩形右下角的x坐标值为baselinex+width;

但需要注意的是:矩形右下角的值并不一定是baselinex+width!它的具体取值是跟paint.setTextAlign(Paint.Align.LEFT)有关的,因为我们这里设置为Paint.Align.LEFT,所以是baselinex+width。如果设置为Paint.Align.CENTER,那么右下角的X坐标值为baselinex+width/2;再者如果设置为Paint.Align.RIGHT,那么右下角的X坐标就是baselineX;所占矩形的四个角的所有位置是与paint.setTextAlign()的设置紧密相关的,至于各个点的计算方法就不再细讲了,根据我们前面讲的paint.setTextAlign()的显示效果是非常容易想到的。

第三部分:画最小区域矩形

//画最小矩形

Rect minRect = new Rect();

paint.getTextBounds(text,0,text.length(),minRect);

minRect.top = baseLineY + minRect.top;

minRect.bottom = baseLineY + minRect.bottom;

paint.setColor(Color.RED);

canvas.drawRect(minRect,paint);

这部分也就没什么难度了,首先根据paint.getTextBounds()得到基线为y=0的最小矩形的各点坐标,然后根据基线得到其实际的top和bottom坐标;然后将其画出来即可

第四部分:画文字

//画文字

paint.setColor(Color.WHITE);

canvas.drawText(text, baseLineX, baseLineY, paint);

四、定点写字

讲完上面的三部分,这篇文章所要讲的知识点基本就结束了,下面就是应用的部分了,在这部分中,我们将讲述,当我们设定一个点,如何到得基线位置,进而画出字符串。

1、给定左上顶点绘图

这部分,我们假定给出所要绘制矩形的左上角顶点坐标,然后画出这个文字。

先来看效果图:

自定义控件之绘图篇:drawText详解

在这个图中,我们给定左上角的位置,即(left,top);我们知道要画文字,drawText()中传进去的Y坐标是基线的位置,所以我们就必须根据top的位置计算出baseline的位置。

我们来看一个公式:

FontMetrics.top = top - baseline;

所以baseline = top - FontMetrics.top;

因为FontMetrics.top是可以得到的,又因为我们的top坐标是给定的,所以通过这个公式就能得到baseline的位置了。

下面举个例子来说明一下根据矩形左上项点绘制文字的过程:

先看下效果图:

自定义控件之绘图篇:drawText详解

在这个效果图中,因为我们会给定矩形左上角顶点(left,top),所以们先画出top线的位置,然后计算出baseline的位置,并画出来。最后根据baseline把文字写出来。

代码如下:

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

String text = "matt's blog";

int top = 200;

int baseLineX = 0 ;

//设置paint

Paint paint = new Paint();

paint.setTextSize(120); //以px为单位

paint.setTextAlign(Paint.Align.LEFT);

//画top线

paint.setColor(Color.parseColor("#196F3D"));

canvas.drawLine(baseLineX, top, 2500, top, paint);

//计算出baseLine位置

Paint.FontMetricsInt fm = paint.getFontMetricsInt();

int baseLineY = top - fm.top;

//画基线

paint.setColor(Color.parseColor("#FF0000"));

canvas.drawLine(baseLineX, baseLineY, 2500, baseLineY, paint);

//画文字

paint.setColor(Color.BLUE);

canvas.drawText(text, baseLineX, baseLineY, paint);

}

这段代码,比较简单,首先是我们给定top给的位置int top = 200;然后根据top线位置计算出baseline所在位置,并画出来.

2、给定中间线位置绘图

先看效果图:

自定义控件之绘图篇:drawText详解

在这个图中,总共有四条线:top线,bottom线,baseline和center线;

图中center线正是在top线和bottom线的正中间。

为了方便推导公式,我另外标了三个距离A,B,C;

很显然,距离A和距离C是相等的,都等于文字所在矩形高度以的一半,即:

A = C = (bottom - top)/2;

又因为bottom = baseline + FontMetrics.bottom;

top = baseline+FontMetrics.top;

所以,将它们两个代入上面的公式,就可得到:

A = C = (FontMetrics.bottom - FontMetrics.top)/2;

而距离B,则表示Center线到baseline的距离。

很显然距离B = C - (bottom - baseline);

又因为

FontMetrics.bottom = bottom-baseline;

C = A;

所以,B = A - FontMetrics.bottom;

所以baseline = center + B = center + A - FontMetrics.bottom = center +

(FontMetrics.bottom - FontMetrics.top)/2 - FontMetrics.bottom;

根据上面的推导过程,我们最终可知,当给定中间线center位置以后,baseline的位置为:

baseline = center + (FontMetrics.bottom - FontMetrics.top)/2 - FontMetrics.bottom;

下面我们举个例子来说明下这个公式的用法。

效果图如下:

自定义控件之绘图篇:drawText详解

代码如下:

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

String text = "matt's blog";

int center = 200;

int baseLineX = 0 ;

//设置paint

Paint paint = new Paint();

paint.setTextSize(120); //以px为单位

paint.setTextAlign(Paint.Align.LEFT);

//画center线

paint.setColor(Color.parseColor("#1A5276"));

paint.setFakeBoldText(true);

paint.setStrokeWidth(3);

canvas.drawLine(baseLineX, center, 3000, center, paint);

//计算出baseLine位置

Paint.FontMetricsInt fm = paint.getFontMetricsInt();

int baseLineY = center + (fm.bottom - fm.top)/2 - fm.bottom;

//画基线

paint.setColor(Color.parseColor("#FF0000"));

paint.setFakeBoldText(true);

paint.setStrokeWidth(3);

canvas.drawLine(baseLineX, baseLineY, 3000, baseLineY, paint);

//画文字

paint.setColor(Color.BLUE);

canvas.drawText(text, baseLineX, baseLineY, paint);

}

这段代码根据给定中间线的位置为200,然后计算出baseline的位置,然后把文字在baseline的基础上画出来。

博客地址:addapp.cn

公众号:addapp

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/u011277123/article/details/70174647

智能推荐

jmu-Java-07多线程-Runnable与匿名类_6-1 jmu-java-07多线程-runnable与匿名类-程序员宅基地

文章浏览阅读902次,点赞2次,收藏3次。题目详情:6-1 jmu-Java-07多线程-Runnable与匿名类 (10 分)在Main方法中启动一个线程t1,该线程输出3行信息:主线程名 线程t1的线程名 线程t1所实现的所有接口。提示:使用System.out.println(Arrays.toString(getClass().getInterfaces()));输出。注:本题也可使用Lambda表达式实现。裁判测试程序:public class Main { public static void_6-1 jmu-java-07多线程-runnable与匿名类

[论文阅读] (07) RAID2020 Cyber Threat Intelligence Modeling Based on Heterogeneous GCN_cyber threat prediction using dynamic heterogeneou-程序员宅基地

文章浏览阅读6.7k次,点赞7次,收藏15次。前一篇文章分享了生成对抗网络GAN的基础知识,包括什么是GAN、常用算法(CGAN、DCGAN、infoGAN、WGAN)、发展历程、预备知识,并通过Keras搭建最简答的手写数字图片生成案例。这篇文章将详细介绍北航老师发表在RAID 2020上的论文《Cyber Threat Intelligence Modeling Based on Heterogeneous Graph Convolutional Network》,基于异构图卷积网络的网络威胁情报建模。希望这篇文章对您有所帮助。_cyber threat prediction using dynamic heterogeneous graph learning

Python三次样条插值与MATLAB三次样条插值简单案例_splev函数matlab-程序员宅基地

文章浏览阅读7.3k次,点赞7次,收藏51次。1 三次样条插值早期工程师制图时,把富有弹性的细长木条(所谓样条)用压铁固定在样点上,在其他地方让它自由弯曲,然后沿木条画下曲线,成为样条曲线。设函数S(x)∈C2[a,b] ,且在每个小区间[xj, xj+1]上是三次多项式,其中a=x0<x1<...<xn=b 是给定节点,则称S(x)是节点x0,x1,...xn上的三次样条函数。若在节点xj上给定函数值yj=f(xj..._splev函数matlab

Hadoop集群搭建所遇到的一些问题_error org.apache.hadoop.security.token.delegation.-程序员宅基地

文章浏览阅读3.9k次,点赞3次,收藏8次。1.命令找不到环境变量配置的不正确.2.权限问题2.1 /home/hadoop目录权限2.1 创建完hadoop用后/home/hadoop目录权限问题./home/Hadoop2.2 秘钥文件权限1.如下图的提示,是当前hadoop用的公钥没有添加到认证文件中去解决方法:将node1节点上的hadoop用户的公钥添加到认证文件中[hadoop@node1 ~]$ chmod -R 755 .ssh/[hadoop@node1 ~]$ cd .ssh/[hadoop@node_error org.apache.hadoop.security.token.delegation.abstractdelegationtokensec

ModuleNotFoundError: No module named ‘ahocorasick‘,亲测100%有效_modulenotfounderror: no module named 'ahocorasick-程序员宅基地

文章浏览阅读2.3k次,点赞10次,收藏10次。ModuleNotFoundError: No module named ‘ahocorasick‘,安装:pip install pyahocorasick -i HTTPS://mirrors.aliyun.com/pypi/simple/下面是我在安装是遇到的要安装最近在搞自然语言处理的知识图谱但是在搭建系统的时候使用了一个AC自动机词过滤,就是去掉敏感词汇就比如这些敏感词汇,需要将他替换成为**等一些隐含词或者符号。这个有许多的方法,这里列举几个:- 1、AC自动机- 2、DFA_modulenotfounderror: no module named 'ahocorasick

随便推点

OpenGLES demo - 11. 透视投影变换_opengles 透视变换-程序员宅基地

文章浏览阅读1.7k次。这章介绍一下透视投影变换,一个物体经过了透视投影变换之后,就会有jindayuan_opengles 透视变换

大数取模运算Barrett reduction-程序员宅基地

文章浏览阅读1.3w次,点赞11次,收藏40次。Barrett reduction算法的证明和使用。作者刚做完了课程设计作业,闲来无事写篇文章。大数中的指数运算,需要对一个数进行取模,因为最大可能二进制下2048位的2048位次方,所以必须边做乘法边取模。乘法使用快速幂,如果底数位数是x,指数位数是e,底数足够大的话,复杂度取决于模数,模数是m位的话,复杂度是O(m*m*e)。程序里,大数的存储是2的32次方进制的,这里..._barrett reduction

MIT 线性代数导论 第二讲:矩阵消元_方程组\left\{ \begin{matrix}3x - 4y = k + 1\\2x + 3y -程序员宅基地

文章浏览阅读367次。第二讲的主要内容:线性方程组的消元法使用矩阵语言表示消元过程向量、矩阵乘的理解置换矩阵的概念初步逆矩阵的概念线性方程组的消元法例子:{x+2y+z=23x+8y+z=124y+z=2\left\{\begin{matrix}x+2y+z=2\\ 3x+8y+z=12\\ 4y+z=2\end{matrix}\right.⎩⎨⎧​x+2y+z=23x+8y+z=12..._方程组\left\{ \begin{matrix}3x - 4y = k + 1\\2x + 3y = 5\end{matrix}\right.{ 3x 4y=k+1 2x+3y=5 的解中x与y的值相等,则k等于( ) a -1 b -2 c -3 d -4 查看提示

macOS下配置环境变量/查看环境变量_mac查看环境变量-程序员宅基地

文章浏览阅读4.8w次,点赞27次,收藏98次。Mac OS 的配置文件清单a. /etc/profile b. /etc/paths c. ~/.bash_profile d. ~/.bash_login e. ~/.profile f. ~/.bashrc 终端如何查看java的安装目录?输入命令:/usr/libexec/java_home -V如何查看java的版本信息?输入命令:java -version如何查看Mac的全部环境变量输入命令 export,查看全部的环境变量如何设置系统环境变量?系统环境变量应该在_mac查看环境变量

table表格------一行隐藏和显示_让row里面的内容成一行显示超出隐藏-程序员宅基地

文章浏览阅读5k次。这个功能是table原生js操作一行表_让row里面的内容成一行显示超出隐藏

使用C#使用Windows的HID通信-程序员宅基地

文章浏览阅读2k次。Windows使用HID通信相对比较简单,HID都是通过PID、VID信息来查找连接的,相比于串口,几乎无变化,连接无需人工选择,十分方便,也不需要驱动。下面上实例,PID为0x003f,VID为0x04D8,支持发送接收数据显示到UI,使用C#来编写,调用的是windows api(create file、read file、write file)。本实例将HID接口分成3层,支持自动连接..._c# hid 指定pid和vid

推荐文章

热门文章

相关标签