Vijay Raghunathan
Vijay Raghunathan

Reputation: 171

Prevent a multiline TextView from unnecessarily expanding to it's maximum width

A TextView that expands to multiple lines seems to automatically expand to fit it's maximum possible width. How can I prevent that from happening?

Here is an example layout (test.xml)

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white">
<TextView
android:background="@color/green"
android:layout_alignParentRight="true"
android:id="@+id/foo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="right"
android:text="This is the message a much shorter message"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="@color/black" />
</RelativeLayout>

TextView shown with maximum width

If I add a margin on the left, it correctly shrinks the width of the TextView (without reformatting the content), but the size of the margin would have to calculated given the contents of the TextView.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white">
<TextView
android:layout_marginLeft="32dp"
android:background="@color/green"
android:layout_alignParentRight="true"
android:id="@+id/foo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="right"
android:text="This is the message a much shorter message"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="@color/black" />
</RelativeLayout>

TextView show with margin

Upvotes: 14

Views: 2846

Answers (4)

Vitaliy Polchuk
Vitaliy Polchuk

Reputation: 1898

You may use custom TextView with overridden method onMeasure() where you calculate the width:

public class WrapWidthTextView extends TextView {

    // ...
    
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        
        Layout layout = getLayout();
        if (layout != null) {
            int width = (int)FloatMath.ceil(getMaxLineWidth(layout))
                    + getCompoundPaddingLeft() + getCompoundPaddingRight();
            int height = getMeasuredHeight();            
            setMeasuredDimension(width, height);
        }
    }
    
    private float getMaxLineWidth(Layout layout) {
        float max_width = 0.0f;
        int lines = layout.getLineCount();
        for (int i = 0; i < lines; i++) {
            if (layout.getLineWidth(i) > max_width) {
                max_width = layout.getLineWidth(i);
            }
        }
        return max_width;
    }
}

Upvotes: 10

CoolMind
CoolMind

Reputation: 28875

If you want to use a suggestion of @Vitaliy Polchuk (a top answer), rewrite so or see his answer at Why is wrap content in multiple line TextView filling parent?:

public class WrapWidthTextView extends AppCompatTextView {
    public WrapWidthTextView(Context context) {
        super(context);
    }

    public WrapWidthTextView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public WrapWidthTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        Layout layout = getLayout();
        if (layout != null) {
            int width = (int) Math.ceil(getMaxLineWidth(layout))
                    + getCompoundPaddingLeft() + getCompoundPaddingRight();
            int height = getMeasuredHeight();
            setMeasuredDimension(width, height);
        }
    }

    private float getMaxLineWidth(Layout layout) {
        float max_width = 0.0f;
        int lines = layout.getLineCount();
        for (int i = 0; i < lines; i++) {
            if (layout.getLineWidth(i) > max_width) {
                max_width = layout.getLineWidth(i);
            }
        }
        return max_width;
    }
}

Rebuild a project when you insert this class into XML. Otherwise it won't find this class.

Upvotes: 1

kasurd
kasurd

Reputation: 476

I've tried the solution of Vitaliy Polchuk and it works fine. And I also want to add small correction:

...
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    Layout layout = getLayout();
    if (layout != null) {
        int width = (int) FloatMath.ceil(getMaxLineWidth(layout))
                + getCompoundPaddingLeft() + getCompoundPaddingRight();
        widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);
    }
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    // get already measured dimensions
    int width = getMeasuredWidth();
    int height = getMeasuredHeight();
    setMeasuredDimension(width, height);
}
...

Upvotes: 0

AliSh
AliSh

Reputation: 10689

use android:padding="32dp" It's help you to have padding, if you want to have padding from left use android:paddingLeft="32dp"

Upvotes: 0

Related Questions