22

I have a custom control that is doing a lot of 2D drawing straight to the canvas.

Some of this drawing is text, so I am using the Canvas.drawText() method.

I want to draw the text within some bounds - a top-left, certain maximum width, and a maximum number of lines. After drawing the text, I want to know how many lines it took.

Is there a built-in function to draw text within bounds doing the splitting sensibly?

If not, is there a standard recipe for doing so?

AjOnFire
  • 2,868
  • 3
  • 25
  • 39
Will
  • 73,905
  • 40
  • 169
  • 246

3 Answers3

34

You can use the android.text.StaticLayout class for this; simply create a StaticLayout for the desired text, alignment, width, etc. and call its draw(Canvas) method to draw to the canvas.

CODE-REaD
  • 2,819
  • 3
  • 33
  • 60
Roman Nurik
  • 29,665
  • 7
  • 84
  • 82
4

You can use Paint.getTextBounds() to measure the size of the entire string or Paint.getTextWidths() to get the width of each character. Then split the string appropriately before drawing it.

Alexander
  • 61
  • 2
4

I had the same problem. One of my first solution is following.

/**
     * This function draws the text on the canvas based on the x-, y-position.
     * If it has to break it into lines it will do it based on the max width
     * provided.
     * 
     * @author Alessandro Giusa
     * @version 0.1, 14.08.2015
     * @param canvas
     *            canvas to draw on
     * @param paint
     *            paint object
     * @param x
     *            x position to draw on canvas
     * @param y
     *            start y-position to draw the text.
     * @param maxWidth
     *            maximal width for break line calculation
     * @param text
     *            text to draw
     */
public static void drawTextAndBreakLine(final Canvas canvas, final Paint paint,
        final float x, final float y, final float maxWidth, final String text) {
    String textToDisplay = text;
    String tempText = "";
    char[] chars;
    float textHeight = paint.descent() - paint.ascent();
    float lastY = y;
    int nextPos = 0;
    int lengthBeforeBreak = textToDisplay.length();
    do {
        lengthBeforeBreak = textToDisplay.length();
        chars = textToDisplay.toCharArray();
        nextPos = paint.breakText(chars, 0, chars.length, maxWidth, null);
        tempText = textToDisplay.substring(0, nextPos);
        textToDisplay = textToDisplay.substring(nextPos, textToDisplay.length());
        canvas.drawText(tempText, x, lastY, paint);
        lastY += textHeight;
    } while(nextPos < lengthBeforeBreak);
}

What is missing:

  • No intelligent break-line mechanism, since it breaks based on the maxWidth

How to call?

    paint.setTextSize(40);
    paint.setColor(Color.WHITE);
    paint.setSubpixelText(true);
    float textHeight = paint.descent() - paint.ascent();
    CanvasUtils.drawTextAndBreakLine(canvas, paint, this.left,
            textHeight, this.displayWidth, this.text);

I have a static class called CanvasUtils where I encapsulate stuff like this. Basically I draw the text within a rectangle. This is the reason textHeight is the height of the text. But you can pass what you want to the function.

Good programming!

Alessandro Giusa
  • 1,660
  • 15
  • 11
  • Yes this worked for me but it forces words to break! Is there a way to fix that? – Eenvincible Nov 07 '15 at 13:05
  • Hey Eenvincible, you mean single words? Hyphenation? – Alessandro Giusa Nov 07 '15 at 13:16
  • Not in particular. what happens with this code is that the text wraps correctly within the rectangle but some words get cut off ending up breaking into new lines with part of the words showing in one line and part of it in the next line. – Eenvincible Nov 07 '15 at 17:06
  • This is what I was saying. And you would like to have an algorithm which is doing hyphenation correctly or an algorithm which is just putting the affected word into the next line? – Alessandro Giusa Nov 08 '15 at 18:35