安卓:为什么View没有maxHeight属性?

206

View有一个minHeight,但一些View却缺少了maxHeight:

我想实现的是让一些项目(View)填充一个ScrollView。当有1..3个项目时,我希望它们直接显示在屏幕上,也就是ScrollView的高度为1、2或3个项目。

当有4个或更多项目时,我希望ScrollView停止扩展(即有一个maxHeight),并开始提供滚动。

然而,很不幸地是没有办法设置maxHeight。因此,我可能需要根据情况在程序中将ScrollView的高度设置为WRAP_CONTENT,当有1..3个项目时,将高度设置为3*sizeOf(View),当有4个或更多项目时。

有人能解释为什么已经有minHeight,为什么没有提供maxHeight吗?

(顺便说一下:一些View,比如ImageView,已经实现了maxHeight。)


我在另一个帖子中发布了解决方案:https://dev59.com/zWMl5IYBdhLWcg3wZWWE#26758657 - JMPergar
2
我已经在http://chintanrathod.com/maxheightscrollview-in-android-using-android-studio/上为此创建了一个解决方案。 - Chintan Rathod
谷歌移除maxHeight真是令人烦恼和不便。现在你必须做很多事情才能得到相同的结果。 - Ian Wambai
18个回答

97

对于我所需要的,这些解决方案都没有起作用。我需要一个设置为wrap_content但具有maxHeight的ScrollView,以便在达到某一点后停止扩展并开始滚动。我只是简单地在ScrollView中覆盖了onMeasure方法。

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    heightMeasureSpec = MeasureSpec.makeMeasureSpec(300, MeasureSpec.AT_MOST);
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

这种方法可能并不适用于所有情况,但对于我的布局来说,它确实给我所需的结果。同时也解决了madhu的评论。

如果滚动视图下面有某些布局,则此方法将无法正常工作 - madhu Mar 5 at 4:36


1
这种方法什么情况下不适用?看起来这是一个非常干净的解决方案。让我想知道为什么他们没有将其实现到XML中。 - Christophe Smet
2
如果您手动将高度设置为240,可能不起作用。我没有检查滚动视图的模式,因为我只需要支持一种特定的模式(wrap_content)。 - whizzle
这个答案有一个编辑说:“如果滚动视图下面有一些布局,那么这个技巧就不起作用了”,但这并不是真的。我有一个与页面底部对齐的布局,这个自定义布局有“android:layout_above='@+...'”,它总是将自定义布局放在底部布局上方 :) - Machine Tribe

77
为了创建一个具有最大高度的 ScrollViewListView,您只需要在其周围创建一个高度为所需最大高度的透明 LinearLayout。然后将 ScrollView 的高度设置为 wrap_content。这将创建一个 ScrollView,它似乎会增长,直到其高度等于父 LinearLayout 的高度。

3
这对于防止有滚动内容的对话框扩展至屏幕的全高度非常有用。 - Kyle Ivey
小建议是使用FrameLayout而不是LinearLayout,但我怀疑这并不重要。 - Kyle Ivey
46
如果滚动视图下方有任何布局存在,则这个技巧不起作用。 - Sreedhu Madhu
3
请问您能提供一些代码吗?它对我来说没有起作用,所以可能是我做错了什么... - android developer
如果下面有按钮,它就无法工作。它会将按钮推出屏幕。 - Li Tian Gong
显示剩余3条评论

51

以下是我用于使其可在xml中自定义的代码:

MaxHeightScrollView.java:

public class MaxHeightScrollView extends ScrollView {

private int maxHeight;
private final int defaultHeight = 200;

public MaxHeightScrollView(Context context) {
    super(context);
}

public MaxHeightScrollView(Context context, AttributeSet attrs) {
    super(context, attrs);
    if (!isInEditMode()) {
        init(context, attrs);
    }
}

public MaxHeightScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    if (!isInEditMode()) {
        init(context, attrs);
    }
}

@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public MaxHeightScrollView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
    super(context, attrs, defStyleAttr, defStyleRes);
    if (!isInEditMode()) {
        init(context, attrs);
    }
}

private void init(Context context, AttributeSet attrs) {
    if (attrs != null) {
        TypedArray styledAttrs = context.obtainStyledAttributes(attrs, R.styleable.MaxHeightScrollView);
        //200 is a defualt value
        maxHeight = styledAttrs.getDimensionPixelSize(R.styleable.MaxHeightScrollView_maxHeight, defaultHeight);

        styledAttrs.recycle();
    }
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    heightMeasureSpec = MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.AT_MOST);
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}

attr.xml

<declare-styleable name="MaxHeightScrollView">
        <attr name="maxHeight" format="dimension" />
    </declare-styleable>

示例布局

<blah.blah.MaxHeightScrollView android:layout_weight="1"
                app:maxHeight="90dp"
                android:layout_width="fill_parent"
                android:layout_height="wrap_content">
                <EditText android:id="@+id/commentField"
                    android:hint="Say Something"
                    android:background="#FFFFFF"
                    android:paddingLeft="8dp"
                    android:paddingRight="8dp"
                    android:gravity="center_vertical"
                    android:maxLines="500"
                    android:minHeight="36dp"
                    android:layout_width="fill_parent"
                    android:layout_height="wrap_content" />
            </blah.blah.MaxHeightScrollView>

创建自定义视图绝对是实现这一目标的最佳方式。 - Bertram Gilfoyle
如果您在滚动视图下方有布局,则此方法可行!完美! - Ragaisis

37

(我知道这并没有直接回答问题,但对于其他寻找最大高度功能的人可能会有帮助)

ConstraintLayout通过以下方式为其子项提供最大高度:

app:layout_constraintHeight_max="300dp"
app:layout_constrainedHeight="true" 
或者
app:layout_constraintWidth_max="300dp"
app:layout_constrainedWidth="true" 

这里有一个示例使用.


3
如果视图在垂直链中(如ConstraintLayout文档中所述),则此方法无效。 - Arno Schoonbee
谢谢提供信息。 - user1506104

10

如果我能的话,我本来会在whizzle的回答中留下评论的,但是我认为有必要注意一下,在Android N的多窗口模式下解决这个问题,我需要稍微改一下代码,就像这样:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    if(MeasureSpec.getSize(heightMeasureSpec) > maxHeight) {
        heightMeasureSpec = MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.AT_MOST);
    }
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

这使得布局可以缩小到小于最大高度,但也防止其变得大于最大高度。我在一个布局类中使用了这个,它覆盖了RelativeLayout,这使我能够创建一个自定义对话框,其中ScrollView作为MaxHeightRelativeLayout的子元素,并且不会扩展到屏幕的全部高度,也会缩小以适应Android N多窗口中的最小窗口大小。


10
如上所述,ConstraintLayout通过以下方式为其子元素提供最大高度:
app:layout_constraintHeight_max="300dp"
app:layout_constrainedHeight="true"

此外,如果一个ConstraintLayout的子元素的最大高度在应用程序运行前是不确定的,仍然有一种方法可以使此子元素自动适应可变高度,无论其在垂直链中放置在何处。
例如,我们需要显示一个底部对话框,其中包括可变高度的标题TextView、可变高度的ScrollView和可变高度的页脚TextView。对话框的最大高度为320dp,当总高度不足320dp时,ScrollView的行为为wrap_content,当总高度超过时,ScrollView的行为为"maxHeight=320dp - header height - footer height"。
我们只需通过xml布局文件即可实现这一点:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="320dp">

    <TextView
        android:id="@+id/tv_header"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/black_10"
        android:gravity="center"
        android:padding="10dp"
        app:layout_constraintBottom_toTopOf="@id/scroll_view"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="1"
        app:layout_constraintVertical_chainStyle="packed"
        tools:text="header" />

    <ScrollView
        android:id="@+id/scroll_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/black_30"
        app:layout_constrainedHeight="true"
        app:layout_constraintBottom_toTopOf="@id/tv_footer"
        app:layout_constraintHeight_max="300dp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/tv_header">

        <LinearLayout
            android:id="@+id/ll_container"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">

            <TextView
                android:id="@+id/tv_sub1"
                android:layout_width="match_parent"
                android:layout_height="160dp"
                android:gravity="center"
                android:textColor="@color/orange_light"
                tools:text="sub1" />

            <TextView
                android:id="@+id/tv_sub2"
                android:layout_width="match_parent"
                android:layout_height="160dp"
                android:gravity="center"
                android:textColor="@color/orange_light"
                tools:text="sub2" />

        </LinearLayout>
    </ScrollView>

    <TextView
        android:id="@+id/tv_footer"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/black_50"
        android:gravity="center"
        android:padding="10dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/scroll_view"
        tools:text="footer" />
</android.support.constraint.ConstraintLayout>

大部分重要的代码都很简短:

app:layout_constraintVertical_bias="1"
app:layout_constraintVertical_chainStyle="packed"

app:layout_constrainedHeight="true"

水平宽度的最大使用方法几乎相同。


4

将您的ScrollView包装在一个普通的LinearLayout周围,其layout_height="max_height",这样就可以完美地完成工作。事实上,我已经使用这段代码生产了5年,没有任何问题。

<LinearLayout
        android:id="@+id/subsParent"
        android:layout_width="match_parent"
        android:layout_height="150dp"
        android:gravity="bottom|center_horizontal"
        android:orientation="vertical">

        <ScrollView
            android:id="@+id/subsScroll"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginBottom="10dp"
            android:layout_marginEnd="15dp"
            android:layout_marginStart="15dp">

            <TextView
                android:id="@+id/subsTv"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/longText"
                android:visibility="visible" />

        </ScrollView>
    </LinearLayout>

1
确实是完美的解决方案。聪明而简单。即使对于recyclerview也很适用。 - Alex Belets
但它将从开始处保持150 dp的高度。 - Mahbubur Rahman Khan

4

无法设置最大高度。但是您可以设置高度。

为了做到这一点,您需要发现scrollView中每个项目的高度。之后,只需将scrollView高度设置为numberOfItens * heightOfItem。

要发现项目的高度,请执行以下操作:

View item = adapter.getView(0, null, scrollView);
item.measure(0, 0);
int heightOfItem = item.getMeasuredHeight();

要设置高度,按照以下步骤操作:
// if the scrollView already has a layoutParams:
scrollView.getLayoutParams().height = heightOfItem * numberOfItens;
// or
// if the layoutParams is null, then create a new one.
scrollView.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, heightOfItem * numberOfItens));

3

我的MaxHeightScrollView自定义视图

public class MaxHeightScrollView extends ScrollView {
    private int maxHeight;

    public MaxHeightScrollView(Context context) {
        this(context, null);
    }

    public MaxHeightScrollView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public MaxHeightScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs);
    }

    private void init(Context context, AttributeSet attrs) {
        TypedArray styledAttrs =
                context.obtainStyledAttributes(attrs, R.styleable.MaxHeightScrollView);
        try {
            maxHeight = styledAttrs.getDimensionPixelSize(R.styleable.MaxHeightScrollView_mhs_maxHeight, 0);
        } finally {
            styledAttrs.recycle();
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if (maxHeight > 0) {
            heightMeasureSpec = MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.AT_MOST);
        }
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
}

style.xml

<declare-styleable name="MaxHeightScrollView">
    <attr name="mhs_maxHeight" format="dimension" />
</declare-styleable>

使用

<....MaxHeightScrollView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:mhs_maxHeight="100dp"
    >

    ...

</....MaxHeightScrollView>

3
我这里有一个答案:

https://dev59.com/zWMl5IYBdhLWcg3wZWWE#29178364

只需创建一个扩展ScrollView的新类并重写其onMeasure方法。

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if (maxHeight > 0){
            int hSize = MeasureSpec.getSize(heightMeasureSpec);
            int hMode = MeasureSpec.getMode(heightMeasureSpec);

            switch (hMode){
                case MeasureSpec.AT_MOST:
                    heightMeasureSpec = MeasureSpec.makeMeasureSpec(Math.min(hSize, maxHeight), MeasureSpec.AT_MOST);
                    break;
                case MeasureSpec.UNSPECIFIED:
                    heightMeasureSpec = MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.AT_MOST);
                    break;
                case MeasureSpec.EXACTLY:
                    heightMeasureSpec = MeasureSpec.makeMeasureSpec(Math.min(hSize, maxHeight), MeasureSpec.EXACTLY);
                    break;
            }
        }

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

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