Popcorn Popper Popcorn Popper - 4 months ago 12
Java Question

Centering and Autosizing Text in Rect

I have a rectangle that I want to draw text inside. I want the text to be centered vertically and horizontally and I need the text size to changed to fit all characters inside the rectangle. Here is my code:

@Override
public void drawFixedText(String text, Rect rect, Paint paint) {
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.FILL);

int cX = rect.left;
int cY = rect.top;

float textSize = paint.getTextSize();
paint.setTextSize(textSize);

float textWidth = paint.measureText(text);

while (textWidth > rect.width()) {
textSize--;
paint.setTextSize(textSize);
}

//if cX and cY are the origin coordinates of the your rectangle
//cX-(textWidth/2) = The x-coordinate of the origin of the text being drawn
//cY+(textSize/2) = The y-coordinate of the origin of the text being drawn

canvas.drawText(text, cX-(textWidth/2), cY+(textSize/2), paint);
}


I tried to combine the answers from Calculate text size according to width of text area and Android draw text into rectangle on center and crop it if needed

But it didn't work in that the text is placed to the left of the rectangle instead of inside of it. How can I fix this?

Answer

First, you need to measure the text width after each time setting its size. Otherwise, you'll end up with an infinite loop if the text starts out wider than the Rect.

while (textWidth > rect.width())  {
    textSize--;
    paint.setTextSize(textSize);
    textWidth = paint.measureText(text);
}

Then, to center the text horizontally, you want to subtract half of the text width from the horizontal midpoint of the Rect, not from its left edge, which is what cX will be in your snippet. That is, replace cX - (textWidth / 2) with rect.centerX() - (textWidth / 2) in the drawText() call.

Furthermore, to center the text vertically, we'll need to do something similar with the y-coordinate. However, using the text size for that will not give the correct result. We need to measure the actual height of the text, which we can do using the Paint#getTextBounds() method.

Altogether, these changes would give something like:

public void drawFixedText(String text, Rect rect, Paint paint) {
    paint.setAntiAlias(true);
    paint.setStyle(Paint.Style.FILL);

    float textSize = paint.getTextSize();
    float textWidth = paint.measureText(text);

    while (textWidth > rect.width())  {
        textSize--;
        paint.setTextSize(textSize);
        textWidth = paint.measureText(text);
    }

    Rect bounds = new Rect();
    paint.getTextBounds(text, 0, text.length(), bounds);

    canvas.drawText(text,
                    rect.centerX() - textWidth / 2,
                    rect.centerY() + bounds.height() / 2,
                    paint);
}

Please note that this assumes a default Paint instance. That is, any properties that affect text alignment have their default values going into this method.