Android Canvas.drawText垂直居中问题三种情况
2016-05-17 22:40
681 查看
官方文档:
public void drawText (String text, float x, float y, Paint paint)
Parameters
text The text to be drawn
x The x-coordinate of the origin of the text being drawn
y The y-coordinate of the origin of the text being drawn
paint The paint used for the text (e.g. color, size, style)
**
Canvas 作为绘制文本时,使用FontMetrics对象,计算位置的坐标。 它的思路和java.awt.FontMetrics的基本相同。
FontMetrics对象它以四个基本坐标为基准,分别为:
FontMetrics.top
FontMetrics.ascent
FontMetrics.descent
FontMetrics.bottom
drawText画字符串是baseline对齐的。所以要特别注意这点,不然画文字可能画到其它地方而误以为没有画出来。
如果baseline对齐的话:底端的Y坐标是:(行高-字体高度)/2+字体高度 ,但是字符串并不居中,经过测试如果:(行高-字体高度)/2+字体高度-6 ,就稍微居中了一点。 以上的方法只是一个取巧的做法,网上也没有找到设置文字居中的方法。
按上面办法会有误差。加上那段距离应该就行了:
**
Canvas绘图,drawText里的origin是以baseline为基准的,直接以目标矩形的bottom传进drawText,字符位置会偏下。这样写代码:
首先自己动手做实验,自己定一个baseline,然后把文字画上去,再画上FontMetrics的几条线。FontMetrics里是字体图样的信息,有float型和int型的版本,都可以从Paint中获取。它的每个成员数值都是以baseline为基准计算的,所以负值表示在baseline之上。实验代码:
红线是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
所以最开始的代码应该改成(顺便加入水平居中):
**
前两种解决方案来自下面两篇博客
http://blog.csdn.net/hursing
/article/8007140.html
public void drawText (String text, float x, float y, Paint paint)
Parameters
text The text to be drawn
x The x-coordinate of the origin of the text being drawn
y The y-coordinate of the origin of the text being drawn
paint The paint used for the text (e.g. color, size, style)
**
方案一:以baseline为基准
**Canvas 作为绘制文本时,使用FontMetrics对象,计算位置的坐标。 它的思路和java.awt.FontMetrics的基本相同。
FontMetrics对象它以四个基本坐标为基准,分别为:
FontMetrics.top
FontMetrics.ascent
FontMetrics.descent
FontMetrics.bottom
Paint textPaint = new Paint( Paint.ANTI_ALIAS_FLAG); textPaint.setTextSize( 35); textPaint.setColor( Color.WHITE); // FontMetrics对象 FontMetrics fontMetrics = textPaint.getFontMetrics(); String text = "abcdefghijklmnopqrstu"; // 计算每一个坐标 float baseX = 0; float baseY = 100; float topY = baseY + fontMetrics.top; float ascentY = baseY + fontMetrics.ascent; float descentY = baseY + fontMetrics.descent; float bottomY = baseY + fontMetrics.bottom; // 绘制文本 canvas.drawText( text, baseX, baseY, textPaint); // BaseLine描画 Paint baseLinePaint = new Paint( Paint.ANTI_ALIAS_FLAG);> baseLinePaint.setColor( Color.RED); canvas.drawLine(0, baseY, getWidth(), baseY, baseLinePaint); // Base描画 canvas.drawCircle( baseX, baseY, 5, baseLinePaint); // TopLine描画 Paint topLinePaint = new Paint( Paint.ANTI_ALIAS_FLAG); topLinePaint.setColor( Color.LTGRAY); canvas.drawLine(0, topY, getWidth(), topY, topLinePaint); // AscentLine描画 Paint ascentLinePaint = new Paint( Paint.ANTI_ALIAS_FLAG); ascentLinePaint.setColor( Color.GREEN); canvas.drawLine(0, ascentY, getWidth(), ascentY, ascentLinePaint); // DescentLine描画 Paint descentLinePaint = new Paint( Paint.ANTI_ALIAS_FLAG); descentLinePaint.setColor( Color.YELLOW); canvas.drawLine(0, descentY, getWidth(), descentY, descentLinePaint); // ButtomLine描画 Paint bottomLinePaint = new Paint( Paint.ANTI_ALIAS_FLAG); bottomLinePaint.setColor( Color.MAGENTA); canvas.drawLine(0, bottomY, getWidth(), bottomY, bottomLinePaint);
drawText画字符串是baseline对齐的。所以要特别注意这点,不然画文字可能画到其它地方而误以为没有画出来。
如果baseline对齐的话:底端的Y坐标是:(行高-字体高度)/2+字体高度 ,但是字符串并不居中,经过测试如果:(行高-字体高度)/2+字体高度-6 ,就稍微居中了一点。 以上的方法只是一个取巧的做法,网上也没有找到设置文字居中的方法。
按上面办法会有误差。加上那段距离应该就行了:
FontMetrics fontMetrics = mPaint.getFontMetrics(); float fontTotalHeight = fontMetrics.bottom - fontMetrics.top; float offY = fontTotalHeight / 2 - fontMetrics.bottom; float newY = baseY + offY; canvas.drawText(text, baseX, newY, paint);
**
方案二:以目标矩形为参照物
**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); }
**
方案三:以某个点为基准居中
**float offset = Math.abs(textPaint.descent() + textPaint.ascent()) / 2 canvas.drawText(str, x, y + offset, paint);
前两种解决方案来自下面两篇博客
http://blog.csdn.net/hursing
/article/8007140.html
相关文章推荐
- Android学习笔记--《第一行代码Android》273页代码解密
- Android 4.4 不休眠+不锁屏+默认中文+去除导航栏
- Android LayoutInflater详解
- 【Android 第三方SDK】第三方推送服务--个推
- Android 调用系统相机 并得到图片
- Android Drawable Resources系列2:<nine-patch>标签、.9.png图片制作
- Android的布局文件的文件名不能包含任何大写字母
- Android实现手势解锁
- android屏幕适配
- Genymotion,高性能的 Android 模拟器
- Activity的生命周期全名解析(一)典型情况下的生命周期
- Android 图片的平移、缩放、倒影、旋转实现
- Android开发中图标像素大小规范
- android 编译笔记
- 第三方Android 模拟器流畅速度快,适合开发人员
- Ubuntu16 android studio
- 第三方Android 模拟器流畅速度快,适合开发人员
- (2)Android学习-------Activity(活动)
- Android Studio的SVN使用详解
- Android加载大图片,LRU缓存机制