Resize Text to Fit the View in Android

A question I have seen frequently over on  stackoverflow.com in the Android section involves how to stretch, shrink, grow or otherwise resize text to fit the size of a view.

This example shows how to use getTextBounds() from the android Paint class to determine the size text will be when drawn.  With this information you can calculate a textSize and textScaleX to use to get your text the size you want.

For this example I am using an ImageButton and extending it to draw the text over the button image.

The full source for this sample can be found in github: BigTextButton source

The goal is to find values for the textSize and textScaleX that will adjust our text size to fill our View.  Instead of calculating this every time onDraw is called, we will watch for size changes and calculate the values up front.  To do this we override onSizeChanged to get the new view width and height.

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);
    // save view size
    mViewWidth = w;
    mViewHeight = h;
    // first determine font point size
    adjustTextSize();
    // then determine width scaling
    // this is done in two steps in case the
    // point size change affects the width boundary
    adjustTextScale();
}

From onSizeChanged we call two methods. First one to determine the textSize we use the getTextBounds() method of the Paint object to determine a size for the text at a known textSize. From there we do a simple calculation to determine what textSize would be used to result in the text bounds that we want.

void adjustTextSize() {
    mTextPaint.setTextSize(100);
    mTextPaint.setTextScaleX(1.0f);
    Rect bounds = new Rect();
    // ask the paint for the bounding rect if it were to draw this
    // text
    mTextPaint.getTextBounds(mText, 0, mText.length(), bounds);
    // get the height that would have been produced
    int h = bounds.bottom - bounds.top;
    // make the text text up 70% of the height
    float target = (float)mViewHeight\*.7f;
    // figure out what textSize setting would create that height
    // of text
    float size = ((target/h)\*100f);
    // and set it into the paint
    mTextPaint.setTextSize(size);
}

The second method determines the textScaleX value in the same way. We find what the size would be with a textScaleX of 1.0, then do some simple math to determine what scale would result in the width of bounds that we want. This is done in a separate call call from the text size calculation just in case setting the textSize affects the result for scale.  Changing textScaleX will definitely not change the height of the text drawn.

void adjustTextScale() {
    // do calculation with scale of 1.0 (no scale)
    mTextPaint.setTextScaleX(1.0f);
    Rect bounds = new Rect();
    // ask the paint for the bounding rect if it were to draw this
    // text.
    mTextPaint.getTextBounds(mText, 0, mText.length(), bounds);
    // determine the width
    int w = bounds.right - bounds.left;
    // calculate the baseline to use so that the
    // entire text is visible including the descenders
    int text\_h = bounds.bottom-bounds.top;
    mTextBaseline=bounds.bottom+((mViewHeight-text\_h)/2);
    // determine how much to scale the width to fit the view
    float xscale = ((float) (mViewWidth-getPaddingLeft()-getPaddingRight())) / w;
    // set the scale for the text paint
    mTextPaint.setTextScaleX(xscale);
}

The final detail is to determine where vertically in the view to paint the text. The boundary rect returned by getTextBounds can help with this. The bounds.top value will actually be a negative value, and it represents the size of the text not including the size of any descenders (parts of letters that extend below the base line). The bounds.bottom value is the size of the descenders. Using this information, we can decide how to position the text in the view. The y value in the call to drawText represents the baseline. Descenders will be drawn below this line. For this example, we adjust the y value so that we are showing the full descenders. This value is saved off as mTextBaseline and is used in onDraw.

@Override
protected void onDraw(Canvas canvas) {
    // let the ImageButton paint background as normal
    super.onDraw(canvas);
    // draw the text
    // position is centered on width
    // and the baseline is calculated to be positioned from the
    // view bottom
    canvas.drawText(mText, mViewWidth/2, mViewHeight-mTextBaseline, mTextPaint);
}

This creates a nice button that automatically adjusts its text size to fill the view.

Once again, the complete source is in github here:  BigTextButton source

Enjoy.