本文介绍了Android 画图:.measureText() 与 .getTextBounds()的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用 Paint.getTextBounds() 测量文本,因为我对获取要呈现的文本的高度和宽度感兴趣.但是实际渲染出来的文本总是比getTextBounds()填充的Rect信息的.width()宽一点.p>

令我惊讶的是,我测试了 .measureText(),发现它返回了一个不同的(更高的)值.我试了一下,发现是对的.

为什么他们报告不同的宽度?如何正确获取高度和宽度?我的意思是,我可以使用 .measureText(),但是我不知道我是否应该信任返回的 .height()getTextBounds().

根据要求,这里是重现问题的最少代码:

final String someText = "你好.我相信我是一些文本!";油漆 p = 新油漆();矩形边界 = new Rect();for (float f = 10; f 

输出显示差异不仅大于 1(并且不是最后一分钟的舍入误差),而且似乎随着大小而增加(我正要得出更多结论,但它可能完全取决于字体):

D/Test (607):大小 10.000000,measureText 135.000000,getTextBounds 134D/测试(607):大小 11.000000,measureText 149.000000,getTextBounds 148D/测试(607):大小 12.000000,measureText 156.000000,getTextBounds 155D/测试(607):大小 13.000000,measureText 171.000000,getTextBounds 169D/测试(607):大小 14.000000,measureText 195.000000,getTextBounds 193D/测试(607):大小 15.000000,measureText 201.000000,getTextBounds 199D/测试(607):大小 16.000000,measureText 211.000000,getTextBounds 210D/测试(607):大小 17.000000,measureText 225.000000,getTextBounds 223D/测试(607):大小 18.000000,measureText 245.000000,getTextBounds 243D/测试(607):大小 19.000000,measureText 251.000000,getTextBounds 249D/测试(607):大小 20.000000,measureText 269.000000,getTextBounds 267D/测试(607):大小 21.000000,measureText 275.000000,getTextBounds 272D/测试(607):大小 22.000000,measureText 297.000000,getTextBounds 294D/测试(607):大小 23.000000,measureText 305.000000,getTextBounds 302D/测试(607):大小 24.000000,measureText 319.000000,getTextBounds 316D/测试(607):大小 25.000000,measureText 330.000000,getTextBounds 326D/测试(607):大小 26.000000,measureText 349.000000,getTextBounds 346D/测试(607):大小 27.000000,measureText 357.000000,getTextBounds 354D/测试(607):大小 28.000000,measureText 369.000000,getTextBounds 365D/测试(607):大小 29.000000,measureText 396.000000,getTextBounds 392D/测试(607):大小 30.000000,measureText 401.000000,getTextBounds 397D/测试(607):大小 31.000000,measureText 418.000000,getTextBounds 414D/测试(607):大小 32.000000,measureText 423.000000,getTextBounds 418D/测试(607):大小 33.000000,measureText 446.000000,getTextBounds 441D/测试(607):大小 34.000000,measureText 455.000000,getTextBounds 450D/测试(607):大小 35.000000,measureText 468.000000,getTextBounds 463D/测试(607):大小 36.000000,measureText 474.000000,getTextBounds 469D/测试(607):大小 37.000000,measureText 500.000000,getTextBounds 495D/测试(607):大小 38.000000,measureText 506.000000,getTextBounds 501D/测试(607):大小 39.000000,measureText 521.000000,getTextBounds 515
解决方案

你可以做我做的检查这样的问题:

学习 Android 源代码,Paint.java 源,同时查看 measureText 和 getTextBounds 方法.您将了解到 measureText 调用 native_measureText,getTextBounds 调用 nativeGetStringBounds,它们是用 C++ 实现的本地方法.

因此,您将继续研究 Paint.cpp,它实现了两者.

native_measureText -> SkPaintGlue::measureText_CII

nativeGetStringBounds -> SkPaintGlue::getStringBounds

现在您的研究将检查这些方法的不同之处.经过一些参数检查后,都调用了 Skia Lib(Android 的一部分)中的函数 SkPaint::measureText,但它们都调用了不同的重载形式.

深入研究 Skia,我发现两个调用都在同一个函数中产生相同的计算,只是返回结果不同.

回答您的问题:您的两个调用都进行相同的计算.结果可能的不同之处在于 getTextBounds 以整数返回边界,而 measureText 返回浮点值.

所以你得到的是在将 float 转换为 int 期间出现舍入错误,这发生在 SkPaintGlue::doTextBounds 调用函数 SkRect::roundOut 中的 Paint.cpp 中.

这两个调用的计算宽度之差可能最大为 1.

2011 年 10 月 4 日编辑

什么可能比可视化更好.我付出了努力,为了自己的探索,为了值得赏金:)

这是60号字体,红色是bounds矩形,紫色是measureText的结果.

可以看出bounds left部分从左边开始一些像素,并且measureText的值在左右都增加了这个值.这就是所谓的 Glyph 的 AdvanceX 值.(我在 SkPaint.cpp 的 Skia 资源中发现了这一点)

所以测试的结果是 measureText 为两边的文本添加了一些提前值,而 getTextBounds 计算给定文本适合的最小边界.

希望这个结果对你有用.

测试代码:

 protected void onDraw(Canvas canvas){final String s = "你好.我是一些文本!";油漆 p = 新油漆();矩形边界 = new Rect();p.setTextSize(60);p.getTextBounds(s, 0, s.length(), bounds);浮动 mt = p.measureText(s);int bw = bounds.width();Log.i("LCG", String.format("measureText %f, getTextBounds %d (%s)",公吨,bw,bounds.toShortString()));bounds.offset(0, -bounds.top);p.setStyle(Style.STROKE);canvas.drawColor(0xff000080);p.setColor(0xffff0000);canvas.drawRect(bounds, p);p.setColor(0xff00ff00);canvas.drawText(s, 0, bounds.bottom, p);}

I'm measuring text using Paint.getTextBounds(), since I'm interested in getting both the height and width of the text to be rendered. However, the actual text rendered is always a bit wider than the .width() of the Rect information filled by getTextBounds().

To my surprise, I tested .measureText(), and found that it returns a different (higher) value. I gave it a try, and found it correct.

Why do they report different widths? How can I correctly obtain the height and width? I mean, I can use .measureText(), but then I wouldn't know if I should trust the .height() returned by getTextBounds().

As requested, here is minimal code to reproduce the problem:

final String someText = "Hello. I believe I'm some text!";

Paint p = new Paint();
Rect bounds = new Rect();

for (float f = 10; f < 40; f += 1f) {
    p.setTextSize(f);

    p.getTextBounds(someText, 0, someText.length(), bounds);

    Log.d("Test", String.format(
        "Size %f, measureText %f, getTextBounds %d",
        f,
        p.measureText(someText),
        bounds.width())
    );
}

The output shows that the difference not only gets greater than 1 (and is no last-minute rounding error), but also seems to increase with size (I was about to draw more conclusions, but it may be entirely font-dependent):

D/Test    (  607): Size 10.000000, measureText 135.000000, getTextBounds 134
D/Test    (  607): Size 11.000000, measureText 149.000000, getTextBounds 148
D/Test    (  607): Size 12.000000, measureText 156.000000, getTextBounds 155
D/Test    (  607): Size 13.000000, measureText 171.000000, getTextBounds 169
D/Test    (  607): Size 14.000000, measureText 195.000000, getTextBounds 193
D/Test    (  607): Size 15.000000, measureText 201.000000, getTextBounds 199
D/Test    (  607): Size 16.000000, measureText 211.000000, getTextBounds 210
D/Test    (  607): Size 17.000000, measureText 225.000000, getTextBounds 223
D/Test    (  607): Size 18.000000, measureText 245.000000, getTextBounds 243
D/Test    (  607): Size 19.000000, measureText 251.000000, getTextBounds 249
D/Test    (  607): Size 20.000000, measureText 269.000000, getTextBounds 267
D/Test    (  607): Size 21.000000, measureText 275.000000, getTextBounds 272
D/Test    (  607): Size 22.000000, measureText 297.000000, getTextBounds 294
D/Test    (  607): Size 23.000000, measureText 305.000000, getTextBounds 302
D/Test    (  607): Size 24.000000, measureText 319.000000, getTextBounds 316
D/Test    (  607): Size 25.000000, measureText 330.000000, getTextBounds 326
D/Test    (  607): Size 26.000000, measureText 349.000000, getTextBounds 346
D/Test    (  607): Size 27.000000, measureText 357.000000, getTextBounds 354
D/Test    (  607): Size 28.000000, measureText 369.000000, getTextBounds 365
D/Test    (  607): Size 29.000000, measureText 396.000000, getTextBounds 392
D/Test    (  607): Size 30.000000, measureText 401.000000, getTextBounds 397
D/Test    (  607): Size 31.000000, measureText 418.000000, getTextBounds 414
D/Test    (  607): Size 32.000000, measureText 423.000000, getTextBounds 418
D/Test    (  607): Size 33.000000, measureText 446.000000, getTextBounds 441
D/Test    (  607): Size 34.000000, measureText 455.000000, getTextBounds 450
D/Test    (  607): Size 35.000000, measureText 468.000000, getTextBounds 463
D/Test    (  607): Size 36.000000, measureText 474.000000, getTextBounds 469
D/Test    (  607): Size 37.000000, measureText 500.000000, getTextBounds 495
D/Test    (  607): Size 38.000000, measureText 506.000000, getTextBounds 501
D/Test    (  607): Size 39.000000, measureText 521.000000, getTextBounds 515
解决方案

You can do what I did to inspect such problem:

Study Android source code, Paint.java source, see both measureText and getTextBounds methods.You'd learn that measureText calls native_measureText, and getTextBounds calls nativeGetStringBounds, which are native methods implemented in C++.

So you'd continue to study Paint.cpp, which implements both.

native_measureText -> SkPaintGlue::measureText_CII

nativeGetStringBounds -> SkPaintGlue::getStringBounds

Now your study checks where these methods differ.After some param checks, both call function SkPaint::measureText in Skia Lib (part of Android), but they both call different overloaded form.

Digging further into Skia, I see that both calls result into same computation in same function, only return result differently.

To answer your question:Both your calls do same computation. Possible difference of result lies in fact that getTextBounds returns bounds as integer, while measureText returns float value.

So what you get is rounding error during conversion of float to int, and this happens in Paint.cpp in SkPaintGlue::doTextBounds in call to function SkRect::roundOut.

The difference between computed width of those two calls may be maximally 1.

EDIT 4 Oct 2011

What may be better than visualization. I took the effort, for own exploring, and for deserving bounty :)

This is font size 60, in red is bounds rectangle, in purple is result of measureText.

It's seen that bounds left part starts some pixels from left, and value of measureText is incremented by this value on both left and right. This is something called Glyph's AdvanceX value. (I've discovered this in Skia sources in SkPaint.cpp)

So the outcome of the test is that measureText adds some advance value to the text on both sides, while getTextBounds computes minimal bounds where given text will fit.

Hope this result is useful to you.

Testing code:

  protected void onDraw(Canvas canvas){
     final String s = "Hello. I'm some text!";

     Paint p = new Paint();
     Rect bounds = new Rect();
     p.setTextSize(60);

     p.getTextBounds(s, 0, s.length(), bounds);
     float mt = p.measureText(s);
     int bw = bounds.width();

     Log.i("LCG", String.format(
          "measureText %f, getTextBounds %d (%s)",
          mt,
          bw, bounds.toShortString())
      );
     bounds.offset(0, -bounds.top);
     p.setStyle(Style.STROKE);
     canvas.drawColor(0xff000080);
     p.setColor(0xffff0000);
     canvas.drawRect(bounds, p);
     p.setColor(0xff00ff00);
     canvas.drawText(s, 0, bounds.bottom, p);
  }

这篇关于Android 画图:.measureText() 与 .getTextBounds()的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-17 14:25