自定义控件之绘图篇(五) —— drawText()详解

前言

概述

四线格与基线

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

这里写图片描述

那么问题来了,在Canvas在利用drawText()绘制文字时,也是有规则的,这个规则就是基线!我们先来看一下什么是基线:

这里写图片描述

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

canvas.drawText()

canvas.drawText()与基线

下面我们来重新看看canvas.drawText()这个函数,有关drawText()的所有函数的基本用法,在文章《自定义控件之绘图篇(二) —— 路径及文字》中已经讲过,这里就不再一一讲解,只拿出一个来讲解下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”这几个字,这个原点坐标应当是下图中绿色小点的位置

这里写图片描述

在(x, y)中最让人捉急的是y坐标,一般而言,(x, y)所代表的位置是所画图形对应的矩形的左上角点,但在drawText()中是非常例外的,y坐标所代表的是基线的位置!

实例

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

首先,新建一个工程blogDrawText,然后自定义一个ViewMyView

public class MyView extends View{  

    public MyView(Context context, AttributeSet attrs) {  
        super(context, attrs);  
    }  
}

然后重写MyViewonDraw()函数,自定义一个基线,然后利用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, 3000, baseLineY, paint);  

    //写文字  
    paint.setColor(Color.GREEN);  
    paint.setTextSize(120); //以px为单位  
    canvas.drawText("harvic\'s blog", baseLineX, baseLineY, paint);  
}

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

首先,我们把(0, 200)所在的这条横线画出来,所以我先画了一条线从点坐标为(0, 200)到点坐标为(3000, 200)的一条直线,然后利用canvas.drawText()以(0, 200)为原点画出文字,在main.xml中添加使用代码:

<?xml version="1.0" encoding="utf-8"?>  
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
              android:orientation="vertical"  
              android:layout_width="fill_parent"  
              android:layout_height="fill_parent"  
        >  

    <com.example.blogDrawText.MyView  
            android:layout_width="fill_parent"  
            android:layout_height="fill_parent"  
            />  
</LinearLayout>

效果图如下:

这里写图片描述

结论:

  1. drawText(String, int, int , Paint)中的参数y(第三个传参)是基线的位置。

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

paint.setTextAlign(Paint.Align.XXX)

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

这里写图片描述

我们知道,我们在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(Align)来设置相对位置来看看结果。

setTextAlign(Paint.Align.LEFT)

在原来代码上加上paint.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, 3000, baseLineY, paint);  

    //写文字  
    paint.setColor(Color.GREEN);  
    paint.setTextSize(120); //以px为单位  
    paint.setTextAlign(Paint.Align.LEFT);  
    canvas.drawText("harvic\'s blog", baseLineX, baseLineY, paint);  
}

结果如下:

这里写图片描述

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

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, 3000, baseLineY, paint);  

    //写文字  
    paint.setColor(Color.GREEN);  
    paint.setTextSize(120); //以px为单位  
    paint.setTextAlign(Paint.Align.CENTER);  
    canvas.drawText("harvic\'s blog", baseLineX, baseLineY, paint);  
}

结果为:

这里写图片描述

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

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, 3000, baseLineY, paint);  

    //写文字  
    paint.setColor(Color.GREEN);  
    paint.setTextSize(120); //以px为单位  
    paint.setTextAlign(Paint.Align.RIGHT);  
    canvas.drawText("harvic\'s blog", baseLineX, baseLineY, paint);  
}

结果为:

这里写图片描述

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

注意

下面,我们再看一个例子:我们只写一个大写字母A,然后将其相对位置为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, 3000, baseLineY, paint);  

    //写文字  
    paint.setColor(Color.GREEN);  
    paint.setTextSize(120); //以px为单位  
    paint.setTextAlign(Paint.Align.CENTER);  
    canvas.drawText("A", baseLineX, baseLineY, paint);  
}

结果如下:

这里写图片描述

我们可以看到字母A在原点(0, 200)的正中间,这也就是我们要强调的:相对位置是根据所要绘制文字所在矩形来计算的

drawText的四线格与FontMetrics

Text的绘图四线格

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

这里写图片描述

除了基线以外,如上图所示,另外还有四条线,分别是ascentdescenttopbottom,它们的意义分别是:

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

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

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

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

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

这里写图片描述

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

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

同样,在这里,我们在绘制文字时,ascent是推荐的绘制文字的最高高度,就表示在绘制文字时,尽力要在这个最高高度以下绘制文字。descent是推荐的绘制文字的最底高度线,同样表示是在绘制文字时尽量在这个descent线以上来绘制文字。而top线则指该文字可以绘制的最高高度线,bottom则是表示该文字可以绘制的最低高度线。

ascentdescent是系统建议上的绘制高度,而topbottom则是物理上屏幕最高,最低可以画的高度值。他们的差别与我们上面说的视频处理的安全框和屏幕的道理是一样的。

FontMetrics

FontMetrics概述

上面我们讲了,系统在画文字时的五条线:baselineascentdescenttopbottom。我们知道baseline的位置是我们在构造drawText()时的参数y来决定的,那ascentdescenttopbottom这些线的位置要怎么计算出来呢?

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坐标

我们再来看个图:

这里写图片描述

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

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

  2. 大家千万不要将FontMetrics中的ascentdescenttopbottom与现实中的ascentdescenttopbottom所混淆!这几条线是真实存在的,而FontMetrics中的ascentdescenttopbottom这个变量的值就是用来计算这几条线的位置的。下面我们将看到如何利用这几个变量来计算这几条线的位置。

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

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变量的值必然是正数。

得到Text四线格的各线位置

下面,我们就来看看如何通过这些变量来得到对应线所在位置吧,我们先列出来一个公式(|fontMetric.ascent|表示取绝对值):

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

因为ascent线的Y坐标等于baseline线的Y坐标减去从baseline线到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

获取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类型的。

实例:计算Text四线格位置

下面,我们就举个例子来看一下如何计算Text绘图中各条线的位置的,先来看一下效果图:

这里写图片描述

在这个例子中,我们先写一行字,然后画出这行字中的top线,ascent线,baseline线,descent线和bottom线,我们直接上完整代码:

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

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

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

有关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()的使用,难度不大,就不再细讲。

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

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

这里写图片描述

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

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

字符串所占高度和宽度

高度

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

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

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

int width = paint.measureText(String text);

使用示例如下:

Paint paint = new Paint();  

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

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

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

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

String text = "harvic\'s blog";  
Paint paint = new Paint();  
//设置paint  
paint.setTextSize(120); //以px为单位  

Rect minRect = new Rect();  
paint.getTextBounds(text,0,text.length(),minRect);  
Log.e("qijian",minRect.toShortString()); 

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

这里写图片描述

可以看到这个矩形的左上角位置为(8, -90),右下角的位置为(634, 26)。

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

我们先来看一个原理:

这里写图片描述

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

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

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

Rect minRect = new Rect();  
paint.getTextBounds(text,0,text.length(),minRect);  
//最小矩形,实际top位置  
int minTop = bounds.top + baselineY;  
//最小矩形,实际bottom位置  
int minBottom = bounds.bottom + baselineY;

下面我们就举个例子来看一下我们列举的这几个函数的使用方法,效果图与这一节开篇时的效果图是一样的,如下:

这里写图片描述

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

protected void onDraw(Canvas canvas) {  
    super.onDraw(canvas);  

    String text = "harvic\'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.BLACK);  
    canvas.drawText(text, baseLineX, baseLineY, paint);  
} 

这段代码看起来比较吓人,但只是看起来,我们分段来看看,这段代码总共分为四部分:设置paint、画字符串所占据矩形、画最小矩形、画文字。

第一部分:设置paint

String text = "harvic\'s blog";  
int baseLineY = 200;  
int baseLineX = 0 ;  

//设置paint  
Paint paint = new Paint();  
paint.setTextSize(120); //以px为单位  
paint.setTextAlign(Paint.Align.LEFT);

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

第二部分:画字符串所占据矩形

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)

然后利用求得到topbottomwidth来得到对应的矩形: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(Align)的设置紧密相关的,至于各个点的计算方法就不再细讲了,根据我们前面讲的paint.setTextAlign(Align)的显示效果是非常容易想到的。

第三部分:画最小矩形

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(String, int, int, Rect)得到基线为y=0的最小矩形的各点坐标,然后根据基线得到其实际的topbottom坐标,然后将其画出来即可。

第四部分:画文字

这部分没什么难度,就不再讲了。

定点写字

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

给定左上顶点绘图

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

这里写图片描述

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

FontMetrics.top = top - baseline
所以baseline = top - FontMetrics.top

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

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

这里写图片描述

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

protected void onDraw(Canvas canvas) {  
    super.onDraw(canvas);  

    String text = "harvic\'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.YELLOW);  
    canvas.drawLine(baseLineX, top, 3000, top, paint);  

    //计算出baseLine位置  
    Paint.FontMetricsInt fm = paint.getFontMetricsInt();  
    int baseLineY = top - fm.top;  

    //画基线  
    paint.setColor(Color.RED);  
    canvas.drawLine(baseLineX, baseLineY, 3000, baseLineY, paint);  

    //写文字  
    paint.setColor(Color.GREEN);  
    canvas.drawText(text, baseLineX, baseLineY, paint);  
}

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

//计算出baseLine位置  
Paint.FontMetricsInt fm = paint.getFontMetricsInt();  
int baseLineY = top - fm.top;  

//画基线  
paint.setColor(Color.RED);  
canvas.drawLine(baseLineX, baseLineY, 3000, baseLineY, paint);

最后就是写出文字了,没什么难度就不讲了。

给定中间线位置绘图

先来看一张图:

这里写图片描述

在这个图中,总共有四条线:top线,bottom线,baselinecenter线。

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

为了方便推导公式,我另外标了三个距离A、B、C。很显然,距离A和距离C是相等的,都等于文字所在矩形高度以的一半,即:

A = C = (bottom - top)/2

又因为bottom = baseline + FontMetrics.bottomtop = 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

下面我们举个例子来说明下这个公式的用法,效果图如下:

这里写图片描述

代码如下:

protected void onDraw(Canvas canvas) {  
    super.onDraw(canvas);  

    String text = "harvic\'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.YELLOW);  
    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.RED);  
    canvas.drawLine(baseLineX, baseLineY, 3000, baseLineY, paint);  

    //写文字  
    paint.setColor(Color.GREEN);  
    canvas.drawText(text, baseLineX, baseLineY, paint);  
}

这段代码也没什么难度,给定中间线的位置为200,然后计算出baseline的位置,然后把文字在baseline的基础上画出来。

最后一部分给定定点计算baseline位置的东东,大家不必刻意去记,没什么用,当大家需要时,自己根据FontMetrics,最小矩形,及各条线的意义,自己来推导就可以了。

好了,有关文字的绘图到这里就结束了。

原文链接:点击这里

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
您好!对于在Qt中自定义件并进行重绘,您可以按照以下步骤进行操作: 1. 创建自定义件类:首先,您需要创建一个继承自QWidget或其子类的自定义件类。您可以使用Qt Designer来设计件的外观,并生成相应的.ui文件。 2. 重写绘制事件:在自定义件类中,重写paintEvent()函数来实现件的绘制操作。在这个函数中,可以使用Qt提供的绘图工具类(如QPainter)来绘制您想要的图形、文本等。 例如,以下是一个简单的自定义件类,名为MyWidget: ```cpp class MyWidget : public QWidget { public: MyWidget(QWidget *parent = nullptr) : QWidget(parent) {} protected: void paintEvent(QPaintEvent *event) override { QPainter painter(this); painter.fillRect(rect(), Qt::white); // 填充背景为白色 painter.setPen(Qt::blue); painter.drawRect(10, 10, width() - 20, height() - 20); // 绘制蓝色边框矩形 painter.drawText(rect(), Qt::AlignCenter, "Hello, World!"); // 绘制文本居中显示 } }; ``` 3. 使用自定义件:在需要使用自定义件的地方,将其实例化,并添加到您的窗口或布局中即可。 ```cpp MyWidget *widget = new MyWidget(this); layout->addWidget(widget); // 假设layout是您的布局对象 ``` 通过以上步骤,您就可以自定义件并重绘它的外观了。当件需要更新时,Qt会自动触发重绘事件,调用paintEvent()函数来重新绘制件。 希望对您有所帮助!如果还有其他问题,请随时提问。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值