这段时间正遇到个自定义view实现文字在固定正方形中居中的例子,网上查找了下方法,发现要实现text居中的算法其实是这样的。
我们先来看看一般的作法:
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的几条线。
- @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非常接近)
网上有文章说 字符本身是在灰线和绿线之间居中的(我赞同这种)
以前看到qianfeng的一个老师说 字符本身是在黄线和红线之间居中的
他给出的效果图![](https://img-blog.csdn.net/20160201134726642?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
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
或是
(targetRect.bottom + targetRect.top - fontMetrics.descent - fontMetrics.ascent) / 2(不大赞同)
所以最开始的代码应该改成(顺便加入水平居中):
效果:Rect targetRect = new Rect(20, 130, 550, 230); Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); paint.setStrokeWidth(3); paint.setTextSize(50); // String testString = "测试:ijkJQKA:1234"; String testString = "Apple"; paint.setColor(Color.CYAN); canvas.drawRect(targetRect, paint); paint.setColor(Color.RED); Paint.FontMetricsInt fontMetrics = paint.getFontMetricsInt(); // int baseline = (targetRect.bottom + targetRect.top - fontMetrics.bottom - fontMetrics.top) / 2; int baseline = (targetRect.bottom + targetRect.top - fontMetrics.ascent - fontMetrics.descent) / 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画文字的算法