Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Setting a maximum width on a ViewGroup

How do I set the maximum width of a ViewGroup? I am using a Theme.Dialog activity, however, this does not look so good when resized to bigger screens, it's also sort of lightweight and I don't want it taking up the whole screen.

I tried this suggestion to no avail. Also, there is no android:maxWidth property like some views.

Is there a way to restrict the root LinearLayout so that it is only (for example) 640 dip? I am willing to change to another ViewGroup for this.

Any suggestions?

like image 588
zsniperx Avatar asked May 03 '11 21:05

zsniperx


4 Answers

One option which is what I did is to extend LinearLayout and override the onMeasure function. For example:

public class BoundedLinearLayout extends LinearLayout {

    private final int mBoundedWidth;

    private final int mBoundedHeight;

    public BoundedLinearLayout(Context context) {
        super(context);
        mBoundedWidth = 0;
        mBoundedHeight = 0;
    }

    public BoundedLinearLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.BoundedView);
        mBoundedWidth = a.getDimensionPixelSize(R.styleable.BoundedView_bounded_width, 0);
        mBoundedHeight = a.getDimensionPixelSize(R.styleable.BoundedView_bounded_height, 0);
        a.recycle();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // Adjust width as necessary
        int measuredWidth = MeasureSpec.getSize(widthMeasureSpec);
        if(mBoundedWidth > 0 && mBoundedWidth < measuredWidth) {
            int measureMode = MeasureSpec.getMode(widthMeasureSpec);
            widthMeasureSpec = MeasureSpec.makeMeasureSpec(mBoundedWidth, measureMode);
        }
        // Adjust height as necessary
        int measuredHeight = MeasureSpec.getSize(heightMeasureSpec);
        if(mBoundedHeight > 0 && mBoundedHeight < measuredHeight) {
            int measureMode = MeasureSpec.getMode(heightMeasureSpec);
            heightMeasureSpec = MeasureSpec.makeMeasureSpec(mBoundedHeight, measureMode);
        }
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
}

Then you XML would use the custom class:

<com.yourpackage.BoundedLinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    app:bounded_width="900dp">

    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
    />
</com.youpackage.BoundedLinearLayout>

And the attr.xml file entry

<declare-styleable name="BoundedView">
    <attr name="bounded_width" format="dimension" />
    <attr name="bounded_height" format="dimension" />
</declare-styleable>

EDIT: This is the actual code I am using now. This is still not complete but works in most cases.

like image 185
Chase Avatar answered Nov 15 '22 20:11

Chase


Here is better code for the Dori's answer.

In method onMeasure, If you call super.onMeasure(widthMeasureSpec, heightMeasureSpec); first in the method then all objects' width in the layout will not be changed. Because they initialized before you set the layout(parent) width.

public class MaxWidthLinearLayout extends LinearLayout {
    private final int mMaxWidth;

    public MaxWidthLinearLayout(Context context) {
        super(context);
        mMaxWidth = 0;
    }

    public MaxWidthLinearLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.MaxWidthLinearLayout);
        mMaxWidth = a.getDimensionPixelSize(R.styleable.MaxWidthLinearLayout_maxWidth, Integer.MAX_VALUE);
        a.recycle();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int measuredWidth = MeasureSpec.getSize(widthMeasureSpec);
        if (mMaxWidth > 0 && mMaxWidth < measuredWidth) {
            int measureMode = MeasureSpec.getMode(widthMeasureSpec);
            widthMeasureSpec = MeasureSpec.makeMeasureSpec(mMaxWidth, measureMode);
        }
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
}

And here is a link for usage of xml attr:
 http://kevindion.com/2011/01/custom-xml-attributes-for-android-widgets/

Thanks for this question and answers. Your answer has helped me a lot, and I hope it helps somebody else in the future as well.

like image 30
Jonypoins Avatar answered Nov 15 '22 22:11

Jonypoins


Building on top of the original answer by Chase (+1) I would make a couple of changes (outlined below).

  1. I would have the max width set via a custom attribute (xml below the code)

  2. I would call super.measure() first and then do the Math.min(*) comparison. Using the original answers code we may encounter problems when the incoming size set in the MeasureSpec is either LayoutParams.WRAP_CONTENT or LayoutParams.FILL_PARENT. As these valid constants have values of -2 and -1 respectivly, the original Math.min(*) becomes useless as it will preserve these vales over the max size, and say the measured WRAP_CONTENT is bigger than our max size this check would not catch it. I imagine the OP was thinking of exact dims only (for which it works great)

    public class MaxWidthLinearLayout extends LinearLayout {
    
    private int mMaxWidth = Integer.MAX_VALUE;
    
    public MaxWidthLinearLayout(Context context) {
    
        super(context);
    }
    
    public MaxWidthLinearLayout(Context context, AttributeSet attrs) {
    
        super(context, attrs);
    
        TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.MaxWidthLinearLayout);
        mMaxWidth = a.getDimensionPixelSize(R.styleable.MaxWidthLinearLayout_maxWidth, Integer.MAX_VALUE);
    }
    
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //get measured height
        if(getMeasuredWidth() > mMaxWidth){
            setMeasuredDimension(mMaxWidth, getMeasuredHeight());
        }
    }
    }
    

and the xml attr

    <!-- MaxWidthLinearLayout -->
    <declare-styleable name="MaxWidthLinearLayout">
        <attr name="maxWidth" format="dimension" />
    </declare-styleable>
like image 30
Dori Avatar answered Nov 15 '22 21:11

Dori


Now android.support.constraint.ConstraintLayout makes it easier. Just wrap your view (of any type) with ConstraintLayout, and set the following attributes to the view:

android:layout_width="0dp"
app:layout_constraintWidth_default="spread"
app:layout_constraintWidth_max="640dp"

http://tools.android.com/recent/constraintlayoutbeta5isnowavailable

like image 10
Zbigniew Malinowski Avatar answered Nov 15 '22 21:11

Zbigniew Malinowski