zoukankan      html  css  js  c++  java
  • Android Canvas drawText实现中文垂直居中

    目标:

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

    问题:

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

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    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之上。实验代码:

    [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("测", 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.top + (targetRect.bottom - targetRect.top) / 2 - (FontMetrics.bottom - FontMetrics.top) / 2 - FontMetrics.top

    解决:

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

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    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.top + (targetRect.bottom - targetRect.top - fontMetrics.bottom + fontMetrics.top) / 2 - fontMetrics.top;  
    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

  • 相关阅读:
    Nginx 部署多个 web 项目(虚拟主机)
    Nginx 配置文件
    Linux 安装 nginx
    Linux 安装 tomcat
    Linux 安装 Mysql 5.7.23
    Linux 安装 jdk8
    Linux 安装 lrzsz,使用 rz、sz 上传下载文件
    springMVC 拦截器
    spring 事务
    基于Aspectj 注解实现 spring AOP
  • 原文地址:https://www.cnblogs.com/exmyth/p/4726831.html
Copyright © 2011-2022 走看看