目标:

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

问题:

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

  1. @Override
  2. public void onDraw (Canvas canvas) {
  3. Rect targetRect = new Rect(50, 50, 1000, 200);
  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之上。实验代码:

  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("测", 0, 1, bounds1);
  10. Rect bounds2 = new Rect();
  11. paint.getTextBounds("测试:ijk", 0, 6, 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

解决:

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

  1. @Override
  2. public void onDraw (Canvas canvas) {
  3. Rect targetRect = new Rect(50, 50, 1000, 200);
  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

Android Canvas drawText实现中文垂直居中的更多相关文章

  1. 关于Android Canvas.drawText方法中的坐标参数的正确解释

    canvas.drawText("www.jcodecraeer.com", x, y, paint);  x和y参数是指定字符串中心的坐标吗?还是左上角的坐标?这个问题的直观印象 ...

  2. Android Canvas.drawText方法中的坐标参数的正确解释

    摘要 canvas.drawText(www.jcodecraeer.com, x, y, paint); x和y参数是指定字符串中心的坐标吗?还是左上角的坐标?这个问题的直观印象应该是左上角的坐标, ...

  3. android Canvas drawText 文字居中

    1首先利用canvas获取画布的宽高, //获取屏幕的宽和高int width = canvas.getWidth();int height = canvas.getHeight(); 2获取文字的宽 ...

  4. android canvas drawtext 字高

    Paint pFont = new Paint(); Rect rect = new Rect(); pFont.getTextBounds("豆", 0, 1, rect); L ...

  5. android中画文字的换行 办法(对于遇到canvas.drawText(String s )无法实现换行问题的解决)

    在使用canvas.drawText()绘制文字的时候,发现,如果需要绘制的文字较长,需要换行,通过在文字中加上“\n"或者”\r\n"都无法实现换行,如果非要使用canvas.d ...

  6. android Canvas 和 Paint用法

    自定义view里面的onDraw方法,在这里我们可以绘制各种图形,onDraw里面有两个API我们需要了解清楚他们的用法:Canvas 和 Paint. Canvas翻译成中文就是画布的意思,Canv ...

  7. 如何“任性”使用Android的drawText()

    Android的canvas上可以画很多基本形状,诸如:圆,矩形,线条等等,其中当属文字即drawText()较难理解和使用(额,这只是就个人感受),下面将慢慢介绍下如何简单使用drawText(). ...

  8. Android Canvas绘图详解(图文)

    编辑推荐:稀土掘金,这是一个针对技术开发者的一个应用,你可以在掘金上获取最新最优质的技术干货,不仅仅是Android知识.前端.后端以至于产品和设计都有涉猎,想成为全栈工程师的朋友不要错过! Andr ...

  9. 【转】Android Canvas绘图详解(图文)

    转自:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2012/1212/703.html Android Canvas绘图详解(图文) 泡 ...

随机推荐

  1. AngularJS学习-初识

    angularJS定义和特点 1.google前端开源框架 2.MVVM(model view view-model)设计模式 : Model将和ViewModel互动(通过$scope对象),将监听 ...

  2. python消息队列snakemq使用总结

    Python 消息队列snakemq总结 最近学习消息总线zeromq,在网上搜了python实现的消息总线模块,意外发现有个消息队列snakemq,于是拿来研究一下,感觉还是很不错的,入手简单使用也 ...

  3. typedef 类型重命名 和 #define 宏定义(1)

    http://www.blogjava.net/jasmine214--love/archive/2010/11/29/339307.html 在现实生活中,信息的概念可能是长度,数量和面积等.在C语 ...

  4. Warning: Invalid argument supplied for foreach()

    经常对提交过来的数据进行双重循环,但是为空时会报错:Warning: Invalid argument supplied for foreach() 如下解决即可:foreach($data[$i]  ...

  5. 各大Oj平台介绍[转]

    1.题库与网站资源题库-在线提交系统(Online Judge)简介   下面是几个比较大的在线提交系统(OnlineJudge)里面有大量历年的竞赛题目,注册一个ID,然后用自己熟悉的语言(一般有P ...

  6. 用VS2010编写的C++程序,在其他电脑上无法运行,提示缺少mfc100.dll的解决办法

    问题: 在自己电脑上用VS2010编写的VC++程序(使用MFC库),不能在其他电脑上运行.双击提示: "无法启动此程序,因为计算机中丢失mfc100.dll 尝试重新安装该程序以解决此问题 ...

  7. bzoj 2331: [SCOI2011]地板 插头DP

    2331: [SCOI2011]地板 Time Limit: 5 Sec  Memory Limit: 128 MBSubmit: 541  Solved: 239[Submit][Status] D ...

  8. 【UVA11324】The Largest Clique (SCC)

    题意: 给一张有向图G,求一个结点数最大的结点集,使得该结点中任意两个结点 u 和 v满足:要么 u 可以到达 v, 要么 v 可以到达 u(u 和 v 相互可达也可以). 分析: Tarjan求SC ...

  9. MFC应用程序创建窗口的过程 good

    MFC应用程序中处理消息的顺序 1.AfxWndProc()      该函数负责接收消息,找到消息所属的CWnd对象,然后调用AfxCallWndProc 2.AfxCallWndProc()  该 ...

  10. 浅析深究什么是SOA?(转)

    http://blog.vsharing.com/fengjicheng/A1059842.html 阅读提示: 本文探讨SOA概念背后的核心内涵,如何将SOA落地的实务方法. 金蝶中间件作为全球领先 ...