Android 中Canvas.drawText实现文字居中

转载 2015年11月20日 15:29:48

目标:

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

问题:

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

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. @Override  
  2. public void onDraw (Canvas canvas) {  
  3.     Rect targetRect = new Rect(50501000200);  
  4.     Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);  
  5.     paint.setStrokeWidth(3);  
  6.     paint.setTextSize(80);  
  7.     String testString = "测试:ijkJQKA:1234";  
  8.     paint.setColor(Color.CYAN);  
  9.     canvas.drawRect(targetRect, paint);  
  10.     paint.setColor(Color.RED);  
  11.     canvas.drawText(testString, targetRect.left, targetRect.bottom, paint);  
  12. }  
会得到难看的结果:


找方案:

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

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. @Override  
  2. public void onDraw (Canvas canvas) {  
  3.     Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);  
  4.     paint.setStrokeWidth(3);  
  5.     paint.setTextSize(80);  
  6.     FontMetricsInt fmi = paint.getFontMetricsInt();  
  7.     String testString = "测试:ijkJQKA:1234";  
  8.     Rect bounds1 = new Rect();  
  9.     paint.getTextBounds("测"01, bounds1);  
  10.     Rect bounds2 = new Rect();  
  11.     paint.getTextBounds("测试:ijk"06, bounds2);  
  12.     // 随意设一个位置作为baseline  
  13.     int x = 200;  
  14.     int y = 400;  
  15.     // 把testString画在baseline上  
  16.     canvas.drawText(testString, x, y, paint);  
  17.     // bounds1  
  18.     paint.setStyle(Style.STROKE);  // 画空心矩形  
  19.     canvas.save();  
  20.     canvas.translate(x, y);  // 注意这里有translate。getTextBounds得到的矩形也是以baseline为基准的  
  21.     paint.setColor(Color.GREEN);          
  22.     canvas.drawRect(bounds1, paint);  
  23.     canvas.restore();  
  24.     // bounds2  
  25.     canvas.save();  
  26.     paint.setColor(Color.MAGENTA);  
  27.     canvas.translate(x, y);  
  28.     canvas.drawRect(bounds2, paint);  
  29.     canvas.restore();  
  30.     // baseline  
  31.     paint.setColor(Color.RED);  
  32.     canvas.drawLine(x, y, 1024, y, paint);  
  33.     // ascent  
  34.     paint.setColor(Color.YELLOW);  
  35.     canvas.drawLine(x, y+fmi.ascent, 1024, y+fmi.ascent, paint);  
  36.     // descent  
  37.     paint.setColor(Color.BLUE);  
  38.     canvas.drawLine(x, y+fmi.descent, 1024, y+fmi.descent, paint);  
  39.     // top  
  40.     paint.setColor(Color.DKGRAY);  
  41.     canvas.drawLine(x, y+fmi.top, 1024, y+fmi.top, paint);  
  42.     // bottom  
  43.     paint.setColor(Color.GREEN);  
  44.     canvas.drawLine(x, y+fmi.bottom, 1024, y+fmi.bottom, paint);  
  45. }  
获得结果:


红线是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

解决:

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

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. @Override  
  2. public void onDraw (Canvas canvas) {  
  3.     Rect targetRect = new Rect(50501000200);  
  4.     Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);  
  5.     paint.setStrokeWidth(3);  
  6.     paint.setTextSize(80);  
  7.     String testString = "测试:ijkJQKA:1234";  
  8.     paint.setColor(Color.CYAN);  
  9.     canvas.drawRect(targetRect, paint);  
  10.     paint.setColor(Color.RED);  
  11.     FontMetricsInt fontMetrics = paint.getFontMetricsInt();  
  12.         // 转载请注明出处:http://blog.csdn.net/hursing  
  13.     int baseline = (targetRect.bottom + targetRect.top - fontMetrics.bottom - fontMetrics.top) / 2;  
  14.     // 下面这行是实现水平居中,drawText对应改为传入targetRect.centerX()  
  15.     paint.setTextAlign(Paint.Align.CENTER);  
  16.     canvas.drawText(testString, targetRect.centerX(), baseline, paint);  
  17. }  
效果(点击查看大图):



还可以去看看android sdk源码,

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



原文链接:http://blog.csdn.net/hursing/article/details/18703599

相关文章推荐

Android 史上最简单的实现Canvas drawText文字垂直居中

Android 史上最简单的实现Canvas drawText文字垂直居中楼主最近在做项目,为了方便,写一个自定义的滑动的Button控件,和ViewPager相关联,需要在View上绘制Text(控...

Android Canvas drawText()文字居中

最近也是在学习自定义控件,以前只自己随便玩了下,一直觉得比较难,正好现在工作轻松,每天闲着就看看书看看帖子学习自定义控件。 这里也跟大家分享下我关于drawText()文字居中的方法. 先附上dra...

android canvas drawText()文字居中

drawText()文字居中方法

Android Canvas drawText实现中文垂直居中

转自:http://blog.csdn.net/hursing/article/details/18703599 目标: 把中文字符绘制到目标矩形的居中位置。 问题:...

Android Canvas drawText实现中文垂直居中

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

canvas drawText文字垂直居中

原文:http://blog.csdn.net/hursing/article/details/18703599 关键代码Paint.FontMetricsInt fontMetrics = mPa...

Android Canvas.drawText垂直居中问题三种情况

官方文档: public void drawText (String text, float x, float y, Paint paint)Parameters text The tex...

android中canvas.drawText参数的介绍以及绘制一个文本居中的案例

转载地址:http://blog.csdn.net/lovexieyuan520/article/details/43153275   在自定义View中,会经常遇到canvas.drawTex...

Android绘图实例(Bitmmap,Canvas,Pain的使用)&动态的在图片上添加文字(canvas.drawText)

近些天学习图像处理,今天看见了一段代码中有Bitmap时,

canvas.drawText(string, xPos, yPos, paint)文本居中的办法

paintText = new Paint(); paintText.setStrokeWidth(1); paintText.setAntiAlias(true); ...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Android 中Canvas.drawText实现文字居中
举报原因:
原因补充:

(最多只允许输入30个字)