在ViewGroup上设置最大宽度

61

如何设置ViewGroup的最大宽度?我正在使用一个Theme.Dialog活动,但是当调整大小到更大的屏幕时,它看起来不太好,而且还有点轻便,我不想让它占据整个屏幕。

我尝试了这个建议,但没有成功。 同时,一些视图中也没有像android:maxWidth属性一样的属性。

有没有办法限制根LinearLayout的宽度,使其仅为(例如)640 dip?我愿意改用另一个ViewGroup。

有什么建议吗?

6个回答

90

其中一种选项是我所做的,即扩展LinearLayout并重写onMeasure函数。例如:

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);
    }
}

那么你的 XML 将使用自定义类:

<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>

还有attr.xml文件条目

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

编辑:这是我现在使用的实际代码。虽然还不完整,但在大多数情况下可以工作。


5
令人难以置信的是,Google没有将“maxWidth”默认包含在内,而我们不得不进行子类化 :-( - Someone Somewhere
2
你是绝对正确的。他们实际上正在利用众包平台:] - Chase
当我添加app_name:bounded_width="900dp"时,出现了错误。 - AHmedRef
@AHmédNet 在你的布局文件顶部元素中添加 xmlns:app_name="http://schemas.android.com/apk/res-auto" 以解决该问题。 - David Passmore

34

这是对Dori答案的更好的代码。

onMeasure方法中,如果你先调用super.onMeasure(widthMeasureSpec, heightMeasureSpec);,那么布局中所有对象的宽度都不会改变。因为它们在你设置布局(父级)宽度之前就已经被初始化了。

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);
    }
}

这里有一个关于使用XML属性的链接:
 http://kevindion.com/2011/01/custom-xml-attributes-for-android-widgets/

感谢这个问题和答案,你的回答对我帮助很大,同时我也希望它能够帮助其他人。


1
这个答案更好。 (还有为什么Dori错过了一个微不足道的错误?) - Zyoo
如果在MaxWidthLinearLayout的布局参数中指定了确切的大小,则内容仍将使用原始宽度...您必须指定MATH_PARENT并将确切的大小管理放在onMeasure(..)中。有点傻但可行。 - Gianluca P.

30

在Chase的原始答案基础上进行改进(+1),我会做出一些更改(如下所述)。

  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());
        }
    }
    }
    

以及XML属性

    <!-- MaxWidthLinearLayout -->
    <declare-styleable name="MaxWidthLinearLayout">
        <attr name="maxWidth" format="dimension" />
    </declare-styleable>

谢谢您的回复。我不再需要这个了,但我相信其他人可能也需要它。如果我觉得需要修改我的代码,我一定会来查看这个! - zsniperx
1
如果是为了未来的自己或者一年后的他人,当我忘记自己所做的事情时 :) - Dori
4
这段代码并没有按预期工作。虽然调整了宽度,但包含元素仍在使用原始宽度。因此内容被截断。Jonypoins的答案有效。 - Gunnar Bernstein

11

现在,android.support.constraint.ConstraintLayout 让它更容易。只需将您的视图(任何类型)包装在 ConstraintLayout 中,并将以下属性设置为视图:

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

约束布局Beta 5现已发布


0
这里有一个纯 Kotlin 的解决方案,还能够使用 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>

然后在你的布局XML中:

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

最大高度可以像宽度一样处理;这里为了简洁起见而省略。

这种模式可以应用于任何ViewGroup子类(例如LinearLayout)。


-3

这里有一个简单的答案,

宽度/高度似乎总是需要一起设置。在我的视图中这个方法可行。

    <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" />

按钮的父级设置为包装内容,因此会缩小,但最大宽度为400(4个按钮)。

3
一个按钮不是一个ViewGroup。ViewGroup没有max属性。按钮是TextView的子类,而TextView具有这些属性。整个问题都是关于如何处理没有max属性的视图。 - dm78

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接