Android Canvas drawText实现中文垂直居中

简介: 目标:把中文字符绘制到目标矩形的居中位置。问题:Android的Canvas绘图,drawText里的origin是以baseline为基准的,直接以目标矩形的bottom传进drawText,字符位置会偏下。

目标:

把中文字符绘制到目标矩形的居中位置。

问题:

Android的Canvas绘图,drawText里的origin是以baseline为基准的,直接以目标矩形的bottom传进drawText,字符位置会偏下。这样写代码:

@Override
public void onDraw (Canvas canvas) {
	Rect targetRect = new Rect(50, 50, 1000, 200);
	Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
	paint.setStrokeWidth(3);
	paint.setTextSize(80);
	String testString = "测试:ijkJQKA:1234";
	paint.setColor(Color.CYAN);
	canvas.drawRect(targetRect, paint);
	paint.setColor(Color.RED);
	canvas.drawText(testString, targetRect.left, targetRect.bottom, paint);
}
会得到难看的结果:


找方案:

首先自己动手做实验,自己定一个baseline,然后把文字画上去,再画上FontMetrics的几条线。FontMetrics里是字体图样的信息,有float型int型的版本,都可以从Paint中获取。它的每个成员数值都是以baseline为基准计算的,所以负值表示在baseline之上。实验代码:

@Override
public void onDraw (Canvas canvas) {
	Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
	paint.setStrokeWidth(3);
	paint.setTextSize(80);
	FontMetricsInt fmi = paint.getFontMetricsInt();
	String testString = "测试:ijkJQKA:1234";
	Rect bounds1 = new Rect();
	paint.getTextBounds("测", 0, 1, bounds1);
	Rect bounds2 = new Rect();
	paint.getTextBounds("测试:ijk", 0, 6, bounds2);
	// 随意设一个位置作为baseline
	int x = 200;
	int y = 400;
	// 把testString画在baseline上
	canvas.drawText(testString, x, y, paint);
	// bounds1
	paint.setStyle(Style.STROKE);  // 画空心矩形
	canvas.save();
	canvas.translate(x, y);  // 注意这里有translate。getTextBounds得到的矩形也是以baseline为基准的
	paint.setColor(Color.GREEN);		
	canvas.drawRect(bounds1, paint);
	canvas.restore();
	// bounds2
	canvas.save();
	paint.setColor(Color.MAGENTA);
	canvas.translate(x, y);
	canvas.drawRect(bounds2, paint);
	canvas.restore();
	// baseline
	paint.setColor(Color.RED);
	canvas.drawLine(x, y, 1024, y, paint);
	// ascent
	paint.setColor(Color.YELLOW);
	canvas.drawLine(x, y+fmi.ascent, 1024, y+fmi.ascent, paint);
	// descent
	paint.setColor(Color.BLUE);
	canvas.drawLine(x, y+fmi.descent, 1024, y+fmi.descent, paint);
	// top
	paint.setColor(Color.DKGRAY);
	canvas.drawLine(x, y+fmi.top, 1024, y+fmi.top, paint);
	// bottom
	paint.setColor(Color.GREEN);
	canvas.drawLine(x, y+fmi.bottom, 1024, y+fmi.bottom, paint);
}
获得结果:


红线是baseline,最上面的灰线是FontMetrics.top,最下面的绿线是FontMetrics.bottom。(绿色的bottom和蓝色的descent非常接近)

从图中可知,字符本身是在灰线和绿线之间居中的,知道这个就好办了。网上说的使用paint.getTextBounds的方法都不靠谱,可以看到对一个“测”字和6个字得到的bounds是不同的,图中的矩形能很好地表示这个函数得到的是字符的边界,而不是字体的边界。

FontMetrics.top的数值是个负数,其绝对值就是字体绘制边界到baseline的距离。
所以如果是把文字画在 FontMetrics高度的矩形中, drawText就应该传入 -FontMetrics.top。
要画在targetRect的居中位置,baseline的计算公式就是:
targetRect.centerY() - (FontMetrics.bottom - FontMetrics.top) / 2 - FontMetrics.top
优化后即:

(targetRect.bottom + targetRect.top - fontMetrics.bottom - fontMetrics.top) / 2

解决:

所以最开始的代码应该改成(顺便加入水平居中):

@Override
public void onDraw (Canvas canvas) {
	Rect targetRect = new Rect(50, 50, 1000, 200);
	Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
	paint.setStrokeWidth(3);
	paint.setTextSize(80);
	String testString = "测试:ijkJQKA:1234";
	paint.setColor(Color.CYAN);
	canvas.drawRect(targetRect, paint);
	paint.setColor(Color.RED);
	FontMetricsInt fontMetrics = paint.getFontMetricsInt();
        // 转载请注明出处:http://blog.csdn.net/hursing
	int baseline = (targetRect.bottom + targetRect.top - fontMetrics.bottom - fontMetrics.top) / 2;
	// 下面这行是实现水平居中,drawText对应改为传入targetRect.centerX()
	paint.setTextAlign(Paint.Align.CENTER);
	canvas.drawText(testString, targetRect.centerX(), baseline, paint);
}
效果(点击查看大图):



还可以去看看android sdk源码,

$android4.2/frameworks/base/corej/ava/android/text/BoringLayout.java是TextView画文字的算法

转载请注明出处:http://blog.csdn.net/hursing

目录
相关文章
|
Android开发
flutter中实现仿Android端的onResume和onPause方法
flutter中实现仿Android端的onResume和onPause方法
|
4月前
|
前端开发 数据可视化 Java
Android用Canvas画一个折线图,并加以简单封装
本文介绍了如何用Java绘制动态折线图,从固定折线图的实现到封装成可复用的组件。首先通过绘制XY坐标轴、添加坐标标签和绘制折线及数据点完成基础折线图。接着,将静态数据替换为动态输入,支持自定义X轴、Y轴和折线数据。代码中包含关键方法如`drawDaxes`(绘制坐标轴)、`drawAxispoint`(绘制坐标点)和`drawbrokenLine`(绘制折线)。最终实现可根据传入数据动态生成折线图,适用于Android开发中的数据可视化场景。
133 0
|
4月前
|
XML 前端开发 Java
Android用Canvas画一个真正能跑的跑马灯
这是一篇关于使用Canvas实现跑马灯效果的技术分享。作者通过一个简单的Demo,展示了如何利用Android的Canvas绘制动态跑马灯。核心思路是通过开启耗时线程让彩色背景旋转,结合坐标计算、渐变色绘制和旋转动画,最终实现动态视觉效果。代码分为三个主要部分:1) 计算View宽高与矩形坐标;2) 绘制背景、跑马灯及幕布;3) 设置旋转逻辑并反向调整幕布。文末附有完整Java代码及XML调用示例,适合初学者学习Canvas绘图原理。
|
前端开发 API Android开发
Android自定义View之Canvas一文搞定
这篇文章介绍了Android自定义View中如何使用Canvas和Paint来绘制图形。Canvas可理解为画布,用于绘制各种形状如文字、点、线、矩形、圆角矩形、圆和弧。常见API包括`drawText()`、`drawPoint()`、`drawLine()`、`drawRect()`等。文章还提到了Canvas的保存、恢复、平移和旋转方法,通过绘制钟表盘的例子展示了如何实际应用。总结关键点:Canvas与Paint结合用于图像绘制,掌握Canvas的基本绘图函数及坐标变换操作是自定义View的关键。
301 0
Android自定义View之Canvas一文搞定
|
前端开发 Android开发
Android 中使用Canvas绘制文字和矩形,将结果呈现在Bitmap上
Android 中使用Canvas绘制文字和矩形,将结果呈现在Bitmap上
321 0
|
缓存 JSON Java
java 实现读取txt文件,反射创建对象,android 手机缓存文件目录
java 实现读取txt文件,反射创建对象,android 手机缓存文件目录
java 实现读取txt文件,反射创建对象,android 手机缓存文件目录
|
Android开发 容器
Android实现面包屑效果,支持Fragment联动
Android实现面包屑效果,支持Fragment联动
|
Android开发
Android实现连线题效果
Android实现连线题效果
|
移动开发 JavaScript Android开发
通过howler.js实现在Android下的微信浏览器自动播放音频
通过howler.js实现在Android下的微信浏览器自动播放音频
720 0
通过howler.js实现在Android下的微信浏览器自动播放音频
|
数据库 Android开发
android 多级下拉菜单实现教程 greendao使用
android 多级下拉菜单实现教程 greendao使用
android 多级下拉菜单实现教程 greendao使用