androidtextviewword-wrapandroid-wrap-content

Android TextView does not hug text tightly with multiple lines, sticks to maxWidth instead


I am creating a chat bubble and I noticed that when you have a TextView with text that spans multiple lines the width of the box locks to the (in this case) maxWidth. This may cause a gap on the right side:

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:maxWidth="100dp"
        android:padding="4dp"
        android:text="this is a pretty short sentence"/>

big right gap

As you can see there is a big white gap on the right. Without the maxWidth it fits on one line and it fits snugly:

snug fit

How do I make it so that when the text spans multiple lines the box still tightly hugs the text? I've tried lots of things, but is this even possible?

Desired result:

desired result

update:

android:justificationMode="inter_word"

Results in the text fitting the box instead of the box fitting the text, which is way uglier:

inter_word t


Solution

  • Turns out it's easy to fix by subclassing TextView and overriding onMeasure(). It even works in the Layout Editor. Also performance is no issue at all:

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        //call super first so you can call getLineCount(), getLineMax()) and getMeasuredHeight() below
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    
        //if more than 1 line set width equal to that of the largest line
        int lineCount = getLayout().getLineCount();
        if (lineCount > 1) {
            //get the width of the largest line
            float lineWidth = 0;
            for (int i = 0; i < lineCount; i++) {
                lineWidth = Math.max(lineWidth, getLayout().getLineMax(i));
            }
            //set largest line width + horizontal padding as width and keep the height the same
            setMeasuredDimension((int) Math.ceil(lineWidth) + getPaddingLeft() + getPaddingRight(), getMeasuredHeight());
        }
    }