Home » Android » Don't wrap text in Android TextView at period in abbreviation

Don't wrap text in Android TextView at period in abbreviation

Posted by: admin June 15, 2020 Leave a comment

Questions:

Is there a way to control where a TextView chooses to wrap its text? The issue that I’m having is that it wants to wrap at periods – so a string like “Available in the U.S. and Canada” could wrap after the period between the U and the S. Is there a way to tell the TextView not to wrap unless there’s a period and a space, or some programmatic way to control the wrapping?

How to&Answers:

I ended up writing my own algorithm to break the text only on whitespace. I had originally used the breakText method of Paint, but was having some issues (that may actually be resolved in this version of the code, but that’s OK). This isn’t my best chunk of code, and it could definitely be cleaned up a bit, but it works. Since I’m overriding TextView, I just call this from the onSizeChanged method to ensure that there’s a valid width.

private static void breakManually(TextView tv, Editable editable)
{
    int width = tv.getWidth() - tv.getPaddingLeft() - tv.getPaddingRight();
    if(width == 0)
    {
        // Can't break with a width of 0.
        return false;
    }

    Paint p = tv.getPaint();
    float[] widths = new float[editable.length()];
    p.getTextWidths(editable.toString(), widths);
    float curWidth = 0.0f;
    int lastWSPos = -1;
    int strPos = 0;
    final char newLine = '\n';
    final String newLineStr = "\n";
    boolean reset = false;
    int insertCount = 0;

    //Traverse the string from the start position, adding each character's
    //width to the total until:
    //* A whitespace character is found.  In this case, mark the whitespace
    //position.  If the width goes over the max, this is where the newline
    //will be inserted.
    //* A newline character is found.  This resets the curWidth counter.
    //* curWidth > width.  Replace the whitespace with a newline and reset 
    //the counter.

    while(strPos < editable.length())
    {
        curWidth += widths[strPos];

        char curChar = editable.charAt(strPos);

        if(((int) curChar) == ((int) newLine))
        {
            reset = true;
        }
        else if(Character.isWhitespace(curChar))
        {
            lastWSPos = strPos;
        }
        else if(curWidth > width && lastWSPos >= 0)
        {
            editable.replace(lastWSPos, lastWSPos + 1, newLineStr);
            insertCount++;
            strPos = lastWSPos;
            lastWSPos = -1;
            reset = true;
        }

        if(reset)
        {
            curWidth = 0.0f;
            reset = false;
        }

        strPos++;
    }

    if(insertCount != 0)
    {
         tv.setText(editable);
    }
}

Answer:

There is a spannable called WrapTogetherSpan. There’s no documentation on it, but it sounds like text inside one shouldn’t wrap.