TextView.onDraw causing endless loop
I want a TextView which adjusts the font size so that the text in the view will automatically expand (or shrink) to fill the full width of the view. I thought I might be able to do this by creating a customised TextView which overrides onDraw() as follows:
public class MaximisedTextView extends TextView {
// (calls to super constructors here...)
开发者_如何学运维@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
TextPaint textPaint = this.getPaint();
Rect bounds = new Rect();
String text = (String)this.getText(); // current text in view
float textSize = this.getTextSize(); // current font size
int viewWidth = this.getWidth() - this.getPaddingLeft() - this.getPaddingRight();
textPaint.getTextBounds(text, 0, text.length(), bounds);
int textWidth = bounds.width();
// reset font size to make text fill full width of this view
this.setTextSize(textSize * viewWidth/textWidth);
}
}
However, this sends the app into an endless loop (with the text size growing and shrinking slightly each time!), so I'm clearly going about it the wrong way. Does the call to setTextSize() trigger an invalidate so that onDraw is called again, endlessly?
Is there a way I can prevent the recursive call (if that's what is happening)? Or should I be going about it a completely different way?
Does the call to setTextSize() trigger an invalidate so that onDraw is called again, endlessly?
Yes, that's probably what is hapening. If you take a look of the source code of setTextSize
you will see that it will call this method:
private void setRawTextSize(float size) {
if (size != mTextPaint.getTextSize()) {
mTextPaint.setTextSize(size);
if (mLayout != null) {
nullLayouts();
requestLayout();
invalidate(); // yeahh... invalidate XD
}
}
}
So, if you are doing the hard work of overriding the onDraw
method, why don't you use directly some of the drawText
methods of the Canvas
class?
That's fantasic! Kudos! I'm still adjusting to this wonderful world we live in where we can just look at the source code to see what's going on...
For the benefit of anyone else who might be interested, I took the shortcut of using the TextPaint.setTextSize() method rather than delving into Canvas.drawText(), so my onDraw() is now as follows:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
TextPaint textPaint = this.getPaint();
Rect bounds = new Rect();
String text = (String)this.getText();
float textSize = this.getTextSize();
int viewWidth = this.getWidth() - this.getPaddingLeft() - this.getPaddingRight();
textPaint.getTextBounds(text, 0, text.length(), bounds);
int textWidth = bounds.width();
float newTextSize = (float)Math.floor(textSize * viewWidth/textWidth);
// note: adapted from TextView.setTextSize(), removing invalidate() call:
// get the raw text size...
Context c = getContext();
Resources r = c==null ? Resources.getSystem() : c.getResources();
int unit = TypedValue.COMPLEX_UNIT_SP;
float rawSize = TypedValue.applyDimension(unit, newTextSize, r.getDisplayMetrics());
// ... and apply it directly to the TextPaint
textPaint.setTextSize(rawSize);
}
... and it works a treat. Thanks!
精彩评论