【问题标题】:measuring text on scaled canvas在缩放的画布上测量文本
【发布时间】:2023-03-13 14:17:01
【问题描述】:

我一直在为文本测量和缩放画布而苦苦挣扎。

当画布未缩放时,getTextBounds 和 measureText 会提供准确的结果。但是,当画布被缩放时,这两种方法都不会提供与打印文本的实际大小相匹配的结果。

为了测试,我使用以下 onDraw 方法创建了 View 的子类:

final float scaling = 0.51f;
final int fontSize = 50;

canvas.scale(scaling, scaling);
font = Typeface.create("Arial", Typeface.NORMAL);

Paint paint = new Paint();
paint.setColor(0xff4444ff);
paint.setTypeface(font);
paint.setTextSize(fontSize);
paint.setAntiAlias(true);

int x = 10;
int y = 100;
final String text = "Lorem ipsum dolor sit amet, consectetur adipisici elit...";
canvas.drawText(text, x, y, paint);

// draw border using getTextBounds

paint.setColor(0xffff0000);
paint.setStyle(Paint.Style.STROKE);
paint.setTypeface(font);
paint.setTextSize(fontSize);
Rect bounds = new Rect();
paint.getTextBounds(text, 0, text.length(), bounds);
bounds.offset(x, y);
paint.setColor(0x80ffff00);
canvas.drawRect(bounds, paint);

// draw border using measureText

float w = paint.measureText(text);
bounds.left = x;
bounds.right = (int) Math.ceil(bounds.left + w);
bounds.top -= 10;
bounds.bottom += 10;
paint.setColor(0x8000ffff);
paint.setPathEffect(new DashPathEffect(new float[] { 10, 10 }, 0));
canvas.drawRect(bounds, paint);

对于缩放 = 0.5,我得到以下输出:

对于缩放 = 0.51,显示以下结果:

黄色实线边框标记从 getTextBounds 传递的矩形,青色虚线矩形使用从 measureText 传递的宽度呈现。

如您所见,缩放 = 0.5 的文本小于测量尺寸,缩放 = 0.51 绘制的文本远大于测量尺寸。

感谢任何帮助!

【问题讨论】:

    标签: android canvas fonts measure typeface


    【解决方案1】:

    好的,刚刚发现了如何规避这个问题。

    问题是 Paint 不知道 Canvas 缩放。因此 measureText 和 getTextBounds 提供未缩放的结果。但是由于字体大小不是线性缩放的(但是,绘制的矩形会),您必须手动弥补该效果。

    所以解决办法是:

    // paint the text as usual
    Paint paint = new Paint();
    paint.setTypeface(font);
    paint.setTextSize(fontSize);
    canvas.drawText(text, x, y, paint);
    
    
    // measure the text using scaled font size and correct the scaled value afterwards
    Paint paint = new Paint();
    paint.setTypeface(font);
    paint.setTextSize(fontSize * scaling);
    float w = paint.measureText(text) / scaling;
    

    【讨论】:

      【解决方案2】:

      使用 Mono for Android 我必须使用显示指标,如下所示:

      public override System.Drawing.SizeF MeasureString(MyFont f, string text)
      {
        Rect r = new Rect();
      
        f.DrawingFont.GetTextBounds(text, 0, text.Length, r);
      
        //Manual scaling using DisplayMetrics due to Android
        //issues for compatibility with older versions
        Android.Util.DisplayMetrics metrics = new Android.Util.DisplayMetrics();
        GetDisplay.GetMetrics(metrics);
      
        return new System.Drawing.SizeF(r.Width(), r.Height() * metrics.Density);
      }
      

      其中 f.DrawingFontAndrodid.Text.TextPaint GetDisplay 是:

      private Display GetDisplay()
      {
        return this.GetSystemService(Android.Content.Context.WindowService).JavaCast<Android.Views.IWindowManager>().DefaultDisplay;
      }
      

      和Java中同样的方法是:

      private Display getDisplay() {
          return ((WindowManager) getContext().getSystemService(
                  Context.WINDOW_SERVICE)).getDefaultDisplay();
      }
      

      【讨论】:

      • 你的修正总是被应用并且不补偿画布中的缩放值,不是吗?其实我并不认为bounding rect的计算不正确,而是字体渲染代码没有根据画布缩放选择合适的字体大小...
      • 是的,它总是计算出不需要缩放画布就需要文本尺寸。
      • 忘了说这主要用于在画布中定位文本,需要知道确切的尺寸和位置。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-05-29
      • 1970-01-01
      • 2012-03-10
      • 2019-01-07
      • 2011-10-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多