DisplayMetrics displayMatrics = new DisplayMetrics();
int width = getWindowManager().getDefaultDisplay().getWidth();
// 기기 해상도에 맞는 텍스트 영역만의 크기
float txtWidth = width - 다른 위젯들의 가로길이(pix) 를 합친 값;
String adjustText = title;
for (int i = 0; i < title.length(); i++) {
rowTitle.setText(title.substring(0, i + 1));
if (rowTitle.getPaint().measureText(title.substring(0, i + 1)) > txtWidth) {
Log.d(Constants.LOG_TAG, "title.substring(0, i+1) : " + title.substring(0, i + 1));
adjustText = title.substring(0, i - 2) + "...";
break;
}
}
return adjustText;
}
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 canuse .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):
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 = newPaint(); Rect bounds = newRect(); 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); }
The mice answer is great... And here is the description of the real problem: The short simple answer is that Paint.getTextBounds(String text, int start, int end, Rect bounds) returns Rect which doesn't starts at (0,0). That is, to get actual width of text that will be set by calling Canvas.drawText(String text, float x, float y, Paint paint) with the same Paint object from getTextBounds() you should add the left position of Rect. Something like that: publicintgetTextWidth(Stringtext, Paint paint){ Rect bounds = new Rect(); paint.getTextBounds(text, 0, end, bounds); intwidth = bounds.left + bounds.width(); returnwidth; } Notice this bounds.left - this the key of the problem. In this way you will receive the same width of text, that you would receive using Canvas.drawText(). And the same function should be for getting height of the text: publicintgetTextHeight(Stringtext, Paint paint){ Rect bounds = new Rect(); paint.getTextBounds(text, 0, end, bounds); intheight = bounds.bottom + bounds.height(); returnheight; } P.s.: I didn't test this exact code, but tested the conception.
I think the results @mice found are missleading. The observations might be correct for the font size of 60 but they turn much more different when the text is smaller. Eg. 10px. In that case the text is actually drawn BEYOND the bounds.
Sourcecode of the screenshot:
@Override
protected void onDraw( Canvas canvas ) {
for( int i = 0; i < 20; i++ ) {
int startSize = 10;
int curSize = i + startSize;
paint.setTextSize( curSize );
String text = i + startSize + " - " + TEXT_SNIPPET;
Rect bounds = new Rect();
paint.getTextBounds( text, 0, text.length(), bounds );
float top = STEP_DISTANCE * i + curSize;
bounds.top += top;
bounds.bottom += top;
canvas.drawRect( bounds, bgPaint );
canvas.drawText( text, 0, STEP_DISTANCE * i + curSize, paint );
}
}
I was also figuring out how to measure text on a canvas. After reading the great post from mice i had some problems on how to measure multiline text. There is no obvious way from these contributions but after some research i cam across the StaticLayout class. It allows you to measure multiline text (text with "\n") and configure much more properties of your text via the associated Paint.
Here is a snippet showing how to measure multiline text:
The wrapwitdh is able to determin if you want to limit your multiline text to a certain width.
Since the StaticLayout.getWidth() only returns this boundedWidth you have to take another step to get the maximum width required by your multiline text. You are able to determine each lines width and the max width is the highest line width of course:
There is another way to measure the text bounds precisely, first you should get the path for the current Paint and text. In your case it should be like this:
In my code it always returns correct and expected values. But, not sure if it works faster than your approach.
This is how I calculated the real dimensions for the first letter (you can change the method header to suit your needs, i.e. instead of char[] use String):
private void calculateTextSize(char[] text, PointF outSize) {
// use measureText to calculate width
float width = mPaint.measureText(text, 0, 1);
// use height from getTextBounds()
Rect textBounds = new Rect();
mPaint.getTextBounds(text, 0, 1, textBounds);
float height = textBounds.height();
outSize.x = width;
outSize.y = height;
}
Note that I'm using TextPaint instead of the original Paint class.
출처: http://baeksupervisor.tistory.com/107
이런 상황이 아주 자주 있다.
TextView 의 layout_height 를 wrap_content 로 한다면 width 를 가득 채운 후 개행 하게 된다.. 대략난감 ㅡㅡ;
1줄로만 나오고 싶다면.. maxLine 을 1 로 하면 된다.
하지만 지가 알아서 나올 수 있는 만큼만 나오고 뒷부분은 짤리게 된다..
사용자로 하여금 "다 나왔구나.." 라는 생각이 들게 한다.
그래서 보통 뒤에 text 가 더 있다는 표시로 "..." 더 찍어주는게 된다.
JAVA 단에서 str.length 로 체크 하자니.. 숫자와 영문과 한글은 그 width 가 각각 다르기 때문에 맞지 않는다.
------------------------- 결론적으로 느껴 졌어~ 넌 나의 사람이 된다는게.. ㅡㅡ; ----------------
TextView 의 Paint 객체를 얻어와 measureText 메소드를 사용하여 현재 TextView 의 text 의 width 값을 얻어온다.