我正在使用 测量文本Paint.getTextBounds(),因为我对获取要呈现的文本的高度和宽度感兴趣。但是,实际呈现的文本总是比.填充.width()的信息宽一点。Rect``getTextBounds()
Paint.getTextBounds()
.width()
Rect``getTextBounds()
令我惊讶的是,我测试.measureText()了 ,发现它返回了不同的(更高的)值。我试了一下,发现是对的。
.measureText()
为什么他们报告不同的宽度?如何正确获取高度和宽度?我的意思是,我 可以 使用.measureText(),但是我不知道我是否应该信任.height()返回的getTextBounds().
.height()
getTextBounds()
根据要求,这里是重现问题的最少代码:
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()) ); }
输出显示差异不仅大于 1(并且没有最后一分钟的舍入误差),而且似乎随着大小而增加(我正要得出更多结论,但它可能完全取决于字体):
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
你可以做我所做的来检查这样的问题:
研究 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 中的 Paint.cpp 中调用函数 SkRect::roundOut。
这两个调用的计算宽度之差可能最大为 1。
编辑 2011 年 10 月 4 日
什么可能比可视化更好。我付出了努力,为了自己的探索,为了值得赏金:)
这是字体大小 60,红色是 边界 矩形,紫色是 measureText 的结果。
可以看出 bounds left 部分从左侧开始一些像素,并且 measureText 的值在左侧和右侧都增加了该值。这就是所谓的 Glyph 的 AdvanceX 值。(我在 SkPaint.cpp 的 Skia 资源中发现了这一点)
所以测试的结果是 measureText 为两边的文本添加了一些高级值,而 getTextBounds 计算给定文本适合的最小边界。
希望这个结果对你有用。
测试代码:
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); }