android canvas.drawText在矩形内文字居中

本文详细解析了如何使用Canvas的drawText方法实现文本在指定矩形内的竖直居中显示,介绍了baseline的概念及计算方法,并对比了FontMetrics与getTextBounds的区别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

参考自:https://blog.youkuaiyun.com/hursing/article/details/18703599

看博客的时候,有看到canvas.drawText()方法,但关于baseline这个点看了好久,也看不懂,于是乎,自己写了个demo,总算搞明白了
如若我们要在一个矩形内,绘制文本,需要文字竖直居中

/**
 * text 要绘制的文字
 * x 文字左端x坐标
 * y 文字基线baseline的y值 
 * paint 画笔
*/
drawText(String text, float x, float y, @NonNull Paint paint)

文字左端的x坐标不用解释,但文字基线baseline的y值是什么呢?
管它是什么,画出来再猜

Rect rect = new Rect(50, 50, 1000, 200);
mPaint.setColor(Color.CYAN);
canvas.drawRect(rect, mPaint);
mPaint.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 16, mContext.getResources().getDisplayMetrics()));
mPaint.setColor(Color.BLACK);
canvas.drawText(str, rect.left, rect.bottom, mPaint);

这里写图片描述
如上图,即为矩形的底边为baseline的效果,其实类似于英文四线作业本的第三根线
那么,我们想把文字居中就要很准确的计算出baseline的y坐标
计算之前我们要再来认识另一个类,FontMetrics
还是先画为敬

int x = 50;
int y = 200;
mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(Color.BLACK);
canvas.drawText(str, x, y, mPaint);
Paint.FontMetrics fontMetrics = mPaint.getFontMetrics();
//绘制baseline
mPaint.setColor(Color.RED);
canvas.drawLine(50, 200, 1000, 200, mPaint);
canvas.translate(x, y);
//画fontMetrics.top位置
mPaint.setColor(Color.YELLOW);
canvas.drawLine(0, fontMetrics.top, 1000, fontMetrics.top, mPaint);
//画fontMetrics.bottom位置
mPaint.setColor(Color.MAGENTA);
canvas.drawLine(0, fontMetrics.bottom, 1000, fontMetrics.bottom, mPaint);

看下效果吧:
这里写图片描述
可以看到,黄线为fontmetrics.top,紫线为fontmetrics.bottom,文字在二者其中并且居中
顺便打印出fontmetrics.top fontmetrics.bottom的值如下:

*******************fontMetrics.top******************-76.04297
*******************fontMetrics.bottom******************19.511719

看到fontMetrics.top的值为负数
FontMetrics是描述给定文本字体的各种属性的类,我自己总结的就是包含了字体绘制边界
fontmetrics.top 给定字体大小的文本高于基准线的最大距离
同样的,fontmetrics.bottom 给定字体大小的文本低于基准线的最大距离
其实,根据上图已经可以看的很清楚了,我解释的可能也不够准确,已经很努力的用我的四级解释了
重点是基线baseline的y坐标为0,我意识到这点的时候,才把博主的博客看懂
回到原来的问题,我们要在给定矩形内绘制文字
baseline = rect.centerY() - (fontmetrics.bottom - fontmetrics.top)/2 - fontmetrics.top
baseline = 矩形的数值方向中心线y值 - 字体可绘制边界竖直方向的一半 + 字体可绘制边界的竖直方向顶部到基线的距离即fontmetrics.top
参照下图更好理解:
这里写图片描述

Rect rect = new Rect(50, 50, 1000, 200);
mPaint.setColor(Color.CYAN);
canvas.drawRect(rect, mPaint);
mPaint.setColor(Color.BLACK);
Paint.FontMetrics fontMetrics = mPaint.getFontMetrics();
float bottomLineY = rect.centerY() - (fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.top;
canvas.drawText(str, rect.left, bottomLineY, mPaint);

效果:居中,RNG冠军,nice!
这里写图片描述
另外,区分下,FontMetrics 和 getTextBounds 的区别

int x = 50;
int y = 200;
mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(Color.BLACK);
canvas.drawText(str, x, y, mPaint);
Paint.FontMetrics fontMetrics = mPaint.getFontMetrics();
Log.d("test", "*******************fontMetrics.top******************" + fontMetrics.top);
Log.d("test", "*******************fontMetrics.bottom******************" + fontMetrics.bottom);
//绘制baseline
mPaint.setColor(Color.RED);
canvas.drawLine(50, 200, 1000, 200, mPaint);
canvas.translate(x, y);
//画fontMetrics.top位置
mPaint.setColor(Color.YELLOW);
canvas.drawLine(0, fontMetrics.top, 1000, fontMetrics.top, mPaint);
//画fontMetrics.bottom位置
mPaint.setColor(Color.MAGENTA);
canvas.drawLine(0, fontMetrics.bottom, 1000, fontMetrics.bottom, mPaint);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(2);
//绘制汉字的字体的字体范围
Rect bounds1 = new Rect();
mPaint.setColor(Color.GREEN);
mPaint.getTextBounds(str, 0, 3, bounds1);
canvas.drawRect(bounds1, mPaint);
//绘制包含英文字体的字体范围
Rect bounds2 = new Rect();
mPaint.setColor(Color.BLUE);
mPaint.getTextBounds(str, 0, 7, bounds2);
canvas.drawRect(bounds2, mPaint);
//通过以上两个矩形对比可知,汉字的文字边框和英文的文字边框不同

老规矩,看图说话
这里写图片描述
我的总结是:
getTextBounds为当前文字实际绘制的最小矩形
FontMetrics为给定大小的文字可绘制边界(区分实际绘制,其实我也不是特别清楚,我觉得可能是因为存在下图的文字,所以可绘制边界要预留其额外的距离)
这里写图片描述
最近越来越觉得,自己要学的东西很多,要得到自己想要的,就要付出足够的努力,以前看别人博客经常会一言不合就被鸡汤,自己也炖一勺尝尝。。。加油吧!

### 回答1: 你可以使用以下代码将 StringGrid1 中的文本居中显示: ``` procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect; State: TGridDrawState); begin StringGrid1.Canvas.Brush.Color := clWhite; // 设置背景颜色为白色 StringGrid1.Canvas.FillRect(Rect); // 填充矩形区域 StringGrid1.Canvas.Font.Color := clBlack; // 设置字体颜色为黑色 StringGrid1.Canvas.TextRect(Rect, Rect.Left + (Rect.Right - Rect.Left - StringGrid1.Canvas.TextWidth(StringGrid1.Cells[ACol, ARow])) div 2, Rect.Top + (Rect.Bottom - Rect.Top - StringGrid1.Canvas.TextHeight(StringGrid1.Cells[ACol, ARow])) div 2, StringGrid1.Cells[ACol, ARow]); // 将文本居中显示 end; ``` 在 StringGrid1 中的 OnDrawCell 事件中添加以上代码即可。 ### 回答2: 想要在Delphi XE中,将StringGrid1.Canvas居中,可以使用以下步骤: 1. 在StringGrid1的属性窗口中,找到Options参数,将goFixedVertLine和goFixedHorzLine属性设置为False。这样可以清除StringGrid1上方和左侧的固定行和列。 2. 在StringGrid1的OnDrawCell事件中,编写以下代码来居中绘制单元格: procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect; State: TGridDrawState); var Text: string; TextRect: TRect; begin if (ACol > 0) and (ARow > 0) then //排除标题行和标题列 begin Text := StringGrid1.Cells[ACol, ARow]; //获取当前单元格的文本 StringGrid1.Canvas.FillRect(Rect); //清空当前单元格 TextRect := Rect; //创建一个与当前单元格大小相同的矩形 DrawText(StringGrid1.Canvas.Handle, PChar(Text), Length(Text), TextRect, DT_SINGLELINE or DT_CENTER or DT_VCENTER); //在矩形中央绘制居中的文本 end; end; 这段代码会在绘制每个单元格之前触发,并且会检查当前单元格是否为标题行和标题列。如果不是,则获取单元格的文本,并在居中矩形中绘制文本。 最后,通过启用StringGrid1的默认绘制功能,可以确保文本居中绘制。 请注意,StringGrid1中的固定列和行会影响绘制效果,可能需要根据实际需求进行微调或设置。 ### 回答3: 要使 Delphi XE 中的 StringGrid1.Canvas 居中,您可以使用以下方法: 1. 通过设置 StringGrid1 的属性 DefaultDrawing 为 False,来自定义绘制单元格内容。 2. 在绘制事件 OnDrawCell 中,使用 Canvas.TextExtent 函数来计算单元格内容的宽度。 3. 根据计算出的宽度,使用 (StringGrid1.Width - 宽度) / 2 的公式计算出居中时的左边距。 4. 在绘制单元格内容前,使用 Canvas.TextOut 函数将左边距加上,使内容居中显示。 下面是一个示例代码: ```pascal procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect; State: TGridDrawState); var cellText: string; cellWidth, leftMargin: Integer; begin if (ACol > 0) and (ARow > 0) then begin cellText := StringGrid1.Cells[ACol, ARow]; cellWidth := StringGrid1.Canvas.TextWidth(cellText); // 计算单元格内容的宽度 leftMargin := (Rect.Width - cellWidth) div 2; // 计算居中时的左边距 StringGrid1.Canvas.TextOut(Rect.Left + leftMargin, Rect.Top, cellText); // 绘制居中的内容 end; end; ``` 这样,StringGrid1 中的单元格内容将居中显示。注意,这个方法仅适用于内容居中显示,如果您需要调整单元格本身的对齐方式,需要设置 CellSizing 为 False 并自行处理绘制事件。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值