1

I have a a textview which as width as fill_parent under the root layout which means it has the width of the screen which is 480.
I am trying to measure the bounds of this text in the text view. So I did this :

Rect rect = new Rect();
textView.getPaint().getTextBounds(text,0,text.length()-1,rect);
Log.v(TAG,"Width : Height ="+rect.width()+" : "+width.height());

But it prints 10278 : 79. Why is it printing the width as 10278 when the scren width itself is 480? If it is assuming it to be a single line and calculating then what is the point of having getTextBounds.

Ashwin
  • 12,691
  • 31
  • 118
  • 190

2 Answers2

2

The documentation states that int end is "1 past the last char in the string to measure." http://developer.android.com/reference/android/graphics/Paint.html#getTextBounds(java.lang.String, int, int, android.graphics.Rect)

You should have:

textView.getPaint().getTextBounds(text, 0, text.length(), rect);

This also applies to Canvas.drawText().

The TextView displaying text would be "wrapping" the text. The method getTextBounds is not aware of the TextView at all.

In fact, if you were to build your own custom View for displaying text, getTextBounds would be very useful for achieving your own text wrapping. The following code does a terrible job of presentation, but iterates over a String, drawing sections of it to a Canvas, effectively wrapping the text.

    private void drawText(Canvas canvas) {

        String text = "Nunc eleifend erat eu nulla varius iaculis. Quisque feugiat justo sit amet" +
                " neque interdum tempor. Curabitur faucibus facilisis tristique. Donec posuere" +
                " viverra magna, eu accumsan sapien congue id. Cras fringilla justo ut lacus" +
                " molestie, et venenatis orci egestas. Vivamus pretium, nisl quis cursus cursus," +
                " dolor eros porta neque, sit amet viverra ante orci non elit. Donec odio neque," +
                " volutpat sit amet dui sit amet, fringilla tempus sapien. Donec sed mollis justo." +
                " In dignissim tincidunt neque, eu luctus justo egestas nec. Donec commodo ut arcu" +
                " vel placerat. Donec ullamcorper justo eget justo commodo hendrerit. Quisque" +
                " commodo imperdiet posuere. Aenean vehicula dui.";

        Paint paint = new Paint();
        paint.setTextSize(30);
        paint.setAntiAlias(true);

        int padding = 20;

        int availWidth = getWidth() - 2 * padding;
        int availHeight = getHeight() - 2 * padding;

        Rect bounds = new Rect();
        int currentTextBottom = 0;
        int firstCharInLine = 0;
        int lastCharInLine = 0;

        outerLoop: while (currentTextBottom < availHeight) {

            while (Character.isWhitespace(text.charAt(firstCharInLine))) {
                lastCharInLine = ++firstCharInLine;
            }

            for (int i = firstCharInLine + 1; i < text.length(); i++) {
                paint.getTextBounds(text, firstCharInLine, i, bounds);

                if (bounds.width() <= availWidth) {
                    if (Character.isWhitespace(text.charAt(i))) {
                        lastCharInLine = i;
                    }
                } else {
                    currentTextBottom += bounds.height();
                    if (firstCharInLine == lastCharInLine) {
                        lastCharInLine = i;
                    }
                    canvas.drawText(text, firstCharInLine, lastCharInLine, padding, currentTextBottom + padding, paint);
                    firstCharInLine = lastCharInLine++;
                    break;
                }
                if (i == text.length() - 1) {
                    currentTextBottom += bounds.height();
                    lastCharInLine = text.length();
                    canvas.drawText(text, firstCharInLine, lastCharInLine, padding, currentTextBottom + padding, paint);
                    break outerLoop;
                }
            }
        }
    }
}

EDIT - as a side note, I've just now noticed a difference between bounds.width() and Paint.measureText()! Totally unrelated to the above code, I was having issues with Paint.Align.Center glitching and not always working as expected. So I removed any text alignment and tried:

canvas.drawText(
        text,
        viewWidth / 2f - bounds.width() / 2f,
        viewHeight / 2f + bounds.height() / 2f,
        paint);

But this doesn't align horizontally properly. When I did this instead:

float textWidth = paint.measureText(text);
canvas.drawText(
        text,
        viewWidth / 2f - textWidth / 2,
        viewHeight / 2f + bounds.height() / 2f,
        paint);

It aligned to my satisfaction :)

Fletcher Johns
  • 1,236
  • 15
  • 20
  • "if(bounds.width()<=avaiWidth)" - Isn't this always true? Why have such a condition? "bounds.width()" is the width of a character, where as availWidth is the total width available. – Ashwin Oct 04 '15 at 05:10
  • No, bounds is calculated for an ever increasing section of the string (from firstCharInLine up to and not including i). At some point it will exceed availWidth, then you wrap the line – Fletcher Johns Oct 04 '15 at 05:20
1

The getTextBounds does not return the size of the View, but the minimal space required to display the full string that you give to it:

Return in bounds (allocated by the caller) the smallest rectangle that encloses all of the characters, with an implied origin at (0,0).

This means that for your 1000 characters, and given the currently configured font in the Paint object, it will require at least 10278 pixels to print them. It does not matter that your text or the Paint object came from the textView.

Sebastian
  • 1,076
  • 9
  • 24
  • @Sebastin - then why have a height attribute. So you saying that it is going to assume that it as a single line text calculate the width? If so how is it different from measureText() method? – Ashwin Aug 17 '15 at 08:05
  • getTextBounds returns the smallest rectangle (not only the width), therefore you will also get the height. You can experiment to see what happens if you include newlines in the text. Re: measureText, check [this](http://stackoverflow.com/questions/7549182/android-paint-measuretext-vs-gettextbounds) – Sebastian Aug 17 '15 at 08:10
  • @Sebastin : Okay. So it's going to the next line only if there is a new line character in the text? And not when the width exceeds the textview width? – Ashwin Aug 17 '15 at 08:12
  • I cannot tell for sure now what it does with newlines, but as I said, you can experiment with different strings to find out for yourself. What it for sure will not do is to take into account the size of your screen. – Sebastian Aug 17 '15 at 08:29