我正在使用Paint.getTextBounds()
测量文本,因为我有兴趣获取要渲染的文本的高度和宽度。但是,实际呈现的文本始终比.width()
填充的Rect
信息的getTextBounds()
宽一些。
令我惊讶的是,我测试了.measureText()
,发现它返回了一个不同的(较高)值。我尝试了一下,发现它是正确的。
他们为什么报告不同的宽度?如何正确获取高度和宽度?我的意思是,我可以使用.measureText()
,但是我不知道我是否应该信任.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的过程中出现舍入错误,并且这种情况发生在调用函数SkRect::roundOut的SkPaintGlue::doTextBounds中的Paint.cpp中。
这两个调用的计算宽度之差最大为1。
编辑2011年10月4日
什么可能比可视化更好。我付出了自己的努力,进行了自己的探索,并得到了应有的赏金:)
这是字体大小60,红色是边界矩形,紫色是measureText的结果。
可以看到,左边的边界从左边开始一些像素,而measureText的值在左边和右边都以此值递增。这就是字形的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);
}