我正在使用 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 期间出现舍入错误,这发生在 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 = "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 Paint : . measureText() 与 .getTextBounds(),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7549182/
我需要计算客户提供的所有文本的长度(以像素为单位)。问题是他们只提供了一种常规字体,而斜体样式必须由Android呈现。一个已知的Android错误是,对于斜体(呈现为斜体)字体,未正确计算TextView的宽度并且文本的末尾被剪切。你知道如何正确计算这些斜体文本的长度吗?到目前为止,我已经尝试了measureText和getTextWidths方法,但我认为该错误出在Android源代码中,因为斜体文本和普通文本的长度相同。我发现当没有斜体字体并且必须渲染时,然后在TextView源代码中,在TextPaint对象上设置了skewX参数。它的值为-0.25f。你知道这个参数是什么吗?
我的应用程序能够准确测量字符串至关重要;我一直在使用Paint.measureText()来执行此操作。不幸的是,在4.4中,此方法已更改为不再返回精确值,而是返回向上舍入的值。有人知道我可以用来精确测量文本的另一种方法,或者有任何其他建议吗?Android来源:Android17returnw*mInvCompatScaling;Android18return(float)Math.ceil(w*mInvCompatScaling); 最佳答案 看起来回退到反射是恢复旧功能的唯一方法。如下所示查找私有(private)本地方法并调
我对offsetWidth()与measureText进行了基准测试,我得到了截然不同的值。他们不应该是一样的吗?它们为什么不同?这是下面的jsfiddle和原始代码:http://jsfiddle.net/WhGk7/2/YourbrowserdoesnotsupporttheHTML5canvastag.textAlign=startvarc=document.getElementById("myCanvas");varctx=c.getContext("2d");//Createaredlineinposition150ctx.strokeStyle="red";ctx.move
我对offsetWidth()与measureText进行了基准测试,我得到了截然不同的值。他们不应该是一样的吗?它们为什么不同?这是下面的jsfiddle和原始代码:http://jsfiddle.net/WhGk7/2/YourbrowserdoesnotsupporttheHTML5canvastag.textAlign=startvarc=document.getElementById("myCanvas");varctx=c.getContext("2d");//Createaredlineinposition150ctx.strokeStyle="red";ctx.move
所以我使用Paint的measureText()方法来测量一段文本的宽度,但是我想根据一定的文本大小来测量文本。假设我想获得一个文本段的宽度,当它占据某个TextView时,它将是20个缩放像素。我尝试了以下方法:Paintpaint=newPaint();paint.setTextSize(20);paint.measureText("sampletext");但是,它似乎不起作用。我相信它会返回相对于较小文本大小的宽度。我觉得我错过了一些会让我扇耳光并大喊大叫的东西。 最佳答案 你需要像这样得到密度乘数:finalfloatde
所以我使用Paint的measureText()方法来测量一段文本的宽度,但是我想根据一定的文本大小来测量文本。假设我想获得一个文本段的宽度,当它占据某个TextView时,它将是20个缩放像素。我尝试了以下方法:Paintpaint=newPaint();paint.setTextSize(20);paint.measureText("sampletext");但是,它似乎不起作用。我相信它会返回相对于较小文本大小的宽度。我觉得我错过了一些会让我扇耳光并大喊大叫的东西。 最佳答案 你需要像这样得到密度乘数:finalfloatde
我正在使用Paint.getTextBounds()测量文本,因为我对获取要呈现的文本的高度和宽度感兴趣。但是,实际呈现的文本总是比.width()宽一点。的RectgetTextBounds()填写的信息.令我惊讶的是,我测试了.measureText(),并发现它返回一个不同的(更高的)值。我试了一下,发现是对的。为什么他们报告不同的宽度?如何正确获取高度和宽度?我的意思是,我可以使用.measureText(),但是我不知道我是否应该信任.height()由getTextBounds()返回.根据要求,这里是重现问题的最少代码:finalStringsomeText="Hello
我正在使用Paint.getTextBounds()测量文本,因为我对获取要呈现的文本的高度和宽度感兴趣。但是,实际呈现的文本总是比.width()宽一点。的RectgetTextBounds()填写的信息.令我惊讶的是,我测试了.measureText(),并发现它返回一个不同的(更高的)值。我试了一下,发现是对的。为什么他们报告不同的宽度?如何正确获取高度和宽度?我的意思是,我可以使用.measureText(),但是我不知道我是否应该信任.height()由getTextBounds()返回.根据要求,这里是重现问题的最少代码:finalStringsomeText="Hello