Setting a maximum width on a ViewGroup

AndroidViewWidthAndroid Linearlayout

Android Problem Overview


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?

Android Solutions


Solution 1 - Android

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.

Solution 2 - Android

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.

Solution 3 - Android

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>

Solution 4 - Android

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

Solution 5 - Android

Here's a pure Kotlin solution, with the added benefit of being able to use android:maxWidth.

BoundedFrameLayout.kt

package example.com

import android.content.Context
import android.util.AttributeSet
import android.widget.FrameLayout
import androidx.core.content.withStyledAttributes
import com.sas.android.common.R

class BoundedFrameLayout @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : FrameLayout(context, attrs, defStyleAttr) {
    private var maxWidth = -1

    init {
        if (attrs != null) {
            context.withStyledAttributes(attrs, R.styleable.BoundedView) {
                maxWidth = getDimensionPixelSize(
                    R.styleable.BoundedView_android_maxWidth, maxWidth
                )
            }
        }
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        var widthSpec = widthMeasureSpec
        if (maxWidth >= 0) {
            val widthSize = MeasureSpec.getSize(widthMeasureSpec)
            if (widthSize > maxWidth) {
                val widthMode = MeasureSpec.getMode(widthMeasureSpec)
                widthSpec = MeasureSpec.makeMeasureSpec(maxWidth, widthMode)
            }
        }
        super.onMeasure(widthSpec, heightMeasureSpec)
    }
}

attrs.xml

<resources>
    <declare-styleable name="BoundedView">
        <attr name="android:maxWidth" />
    </declare-styleable>
</resources>

Then in your layout XML:

…
<com.example.BoundedFrameLayout
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    android:maxWidth="350dp"
/>
…

Max height can be handled in the same way as width; it is omitted here for brevity.

This pattern can be adopted for any ViewGroup subclass (e.g. LinearLayout).

Solution 6 - Android

Here is a simple answer,

Width/Height seem to always have to be set together. This is working in my view.

    <Button
        android:text="Center"
        android:layout_width="100dp"
        android:layout_height="fill_parent"
        android:id="@+id/selectionCenterButton"
        android:minWidth="50dp"
        android:minHeight="50dp"
        android:maxWidth="100dp"
        android:maxHeight="50dp"
        android:layout_weight="1" />

The button's parent is set to wrap content, so scales down, but up to a max of 400 wide (4 buttons).

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionzsniperxView Question on Stackoverflow
Solution 1 - AndroidChaseView Answer on Stackoverflow
Solution 2 - AndroidJonypoinsView Answer on Stackoverflow
Solution 3 - AndroidDoriView Answer on Stackoverflow
Solution 4 - AndroidZbigniew MalinowskiView Answer on Stackoverflow
Solution 5 - AndroidStephen TalleyView Answer on Stackoverflow
Solution 6 - AndroiddanielView Answer on Stackoverflow