关闭

Android Canvas drawText实现中文垂直居中

标签: androidcanvasdrawTextbaseline垂直居中
60480人阅读 评论(12) 收藏 举报
分类:

目标:

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

问题:

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

26
0
查看评论

android canvas drawText()文字居中

drawText()文字居中方法
  • zly921112
  • zly921112
  • 2015-12-25 11:09
  • 13240

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

在自定义View中,会经常遇到canvas.drawText这个方法,很多新手都不明白这个方法参数的含义,经过我不断的Google和多次的测试,把我的经验写出来供大家参考,错误之处,欢迎大家给我留言,一起讨论。       首先,我们看下这个方法参数的含义:canvas...
  • lovexieyuan520
  • lovexieyuan520
  • 2015-01-26 16:20
  • 50765

DrawText多行文本垂直居中

要使用DrawText后的文本居中,可以使用DT_CENTER,要垂直居中,直接|DT_VCENTER,或者再加上|DT_SINGLELINE,但是这样只对单行文本有效,多行带换行的文本怎么实现DrawText文本处在最中间的位置呢?网上找到一些相关资料可以实现这样的功能 方法
  • winnyrain
  • winnyrain
  • 2011-08-28 17:35
  • 8745

drawText参数介绍及绘制居中文本

于 2015-09-10 首发在Hyman’s Blog 在android自定义控件中经常用到canvas.drawText方法来绘制文本,我自己在用这个方法的时候,有很多迷惑,首先对参数含义不明白,其次对实现文本居中的计算公式也不太理解。 经过查阅API文档和相关博文,终于算是对这两个疑点理解...
  • ldstartnow
  • ldstartnow
  • 2016-10-19 20:18
  • 490

DrawText上下左右居中输出文字

CView OnDraw:设置字体,设置背景色,设置输出文字颜色,居中输出 // TODO: add draw code for native data here CDocument* pDoc = GetDocument(); CPaintDC* dc = (CPaintDC*)pDC;...
  • CSDNMicrosoftCSDN
  • CSDNMicrosoftCSDN
  • 2016-04-19 10:23
  • 2627

android中利用Paint绘制文本使其居中

要使文字垂直居中,可利用canvas的drawText(text, x, y, paint)方法,其中第三个参数y应该是baseline的值,计算公式如下:int baseline = (getMeasuredHeight() - (paint.descent() - paint.ascent())...
  • AllenIrving
  • AllenIrving
  • 2016-10-09 16:06
  • 788

Canvas上写字drawText -- 确定字符串居中位置

在之前的一篇文章里实现了如何在图片上写字的效果,Android画圆角矩形图片,并在图片上写字 现在有了个新的需求,需要在写出来的字中,有颜色变化。比如要突出统计一个班级的党员人数,例如:软件0901班(10)。其中括号中的10为党员人数。 明确了需求,我们就要想解决的方法。 首先,要写的字符串...
  • Y09lin
  • Y09lin
  • 2013-12-03 10:47
  • 1204

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

前言:但行好事,莫问前程。只需努力每一天。一、概述1、四线格与基线小时候,我们在刚开始学习写字母时,用的本子是四线格的,我们必须把字母按照规则写在四线格内。比如:那么问题来了,在canvas在利用drawText绘制文字时,也是有规则的,这个规则就是基线!我们先来看一下什么是基线:可见基线就是四线格...
  • harvic880925
  • harvic880925
  • 2015-12-29 09:10
  • 25365

Android 自定义View-怎么绘制居中文本?

自定义View,绘制水平/垂直居中文本。精确度无限接近原生TextView
  • u014702653
  • u014702653
  • 2016-07-21 19:13
  • 7786

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

转载地址:http://blog.csdn.net/lovexieyuan520/article/details/43153275   在自定义View中,会经常遇到canvas.drawText这个方法,很多新手都不明白这个方法参数的含义,经过我不断的Google和多次的测试,把我...
  • lijinhua7602
  • lijinhua7602
  • 2015-08-27 16:46
  • 438
    个人资料
    • 访问:1109557次
    • 积分:9947
    • 等级:
    • 排名:第2088名
    • 原创:128篇
    • 转载:0篇
    • 译文:0篇
    • 评论:439条
    联系方式
    微信:hursing

    前阿里巴巴技术专家。现参与互联网金融创业,广招程序员~
    博客专栏
    最新评论