Android Paint: .measureText() vs .getTextBounds()
My experience with this is that getTextBounds
will return that absolute minimal bounding rect that encapsulates the text, not necessarily the measured width used when rendering. I also want to say that measureText
assumes one line.
In order to get accurate measuring results, you should use the StaticLayout
to render the text and pull out the measurements.
For example:
String text = "text";
TextPaint textPaint = textView.getPaint();
int boundedWidth = 1000;
StaticLayout layout = new StaticLayout(text, textPaint, boundedWidth , Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false);
int height = layout.getHeight();
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);
}