Setting a maximum width on a ViewGroup
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.
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.
Building on top of the original answer by Chase (+1) I would make a couple of changes (outlined below).
I would have the max width set via a custom attribute (xml below the code)
I would call
super.measure()
first and then do theMath.min(*)
comparison. Using the original answers code we may encounter problems when the incoming size set in theMeasureSpec
is eitherLayoutParams.WRAP_CONTENT
orLayoutParams.FILL_PARENT
. As these valid constants have values of -2 and -1 respectivly, the originalMath.min(*)
becomes useless as it will preserve these vales over the max size, and say the measuredWRAP_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);}@Overrideprotected 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>