Android: почему нет maxHeight для просмотра?


184

Вид есть,minHeight но почему-то не хватает maxHeight:

То, чего я пытаюсь добиться, - это заполнить некоторые элементы (представления) ScrollView. Когда есть элементы 1..3, я хочу отобразить их напрямую. Значение ScrollViewимеет высоту 1, 2 или 3 элементов.

Когда есть 4 или более элементов, я хочу ScrollViewпрекратить расширение (таким образом, а maxHeight) и начать предоставлять прокрутку.

Однако, к сожалению, нет способа установить maxHeight. Поэтому мне, вероятно, придется программно устанавливать мою ScrollViewвысоту, WRAP_CONTENTкогда есть 1..3 элемента, и устанавливать высоту, 3*sizeOf(View)когда есть 4 или более элементов.

Может кто-нибудь объяснить, почему нет maxHeightпри условии, когда уже есть minHeight?

(Кстати: некоторые взгляды, например, ImageViewимеют maxHeightреализованный.)


Я разместил решение в другой теме: stackoverflow.com/questions/18425758/…
JMPergar

2
Я создал решение для этого на chintanrathod.com/…
Chintan Rathod

Гугл удаление maxHeight раздражает и неудобно. Посмотрите на все вещи, которые вы должны сделать сейчас, чтобы получить тот же результат.
Ян Вамбай

Ответы:


86

Ни одно из этих решений не работало для того, что мне было нужно, для ScrollView было установлено значение wrap_content, но имел maxHeight, чтобы он прекратил расширение после определенной точки и начал прокрутку. Я просто переопределил метод onMeasure в ScrollView.

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

Это может работать не во всех ситуациях, но, безусловно, дает мне результаты, необходимые для моего макета. И это также обращается к комментарию Madhu.

Если какой-то макет присутствует ниже прокрутки, тогда этот трюк не сработает - Мадху 5 марта в 4:36


1
Когда это не сработает? Это кажется очень чистым решением. Меня удивляет, почему они не внедрили это в XML.
Кристоф Смет

1
Вероятно, это не сработает, если вы вручную установите высоту, скажем, 240. Я не стал проверять режим, в котором находится scrollview, потому что мне нужно было только поддерживать один конкретный режим (wrap_content).
Whizzle

Для этого ответа есть правка, которая гласит: «Если какой-то макет присутствует под видом прокрутки, этот трюк не сработает», что неверно. У меня есть макет, выровненный по нижней части страницы, и этот пользовательский макет имеет «android: layout_above = '@ + ...'», который всегда размещает пользовательский макет над нижним макетом :)
Племя машин

70

Чтобы создать maxHeight ScrollViewили ListViewзначение maxHeight, вам просто нужно создать прозрачную линейную схему вокруг него с высотой, на которую вы хотите рассчитать maxHeight. Затем вы устанавливаете высоту ScrollView на wrap_content. Это создает ScrollView, который, кажется, растет, пока его высота не станет равной родительскому LinearLayout.


3
Это было полезно для предотвращения расширения диалогового окна с прокручиваемым содержимым до полной высоты экрана.
Кайл Айви

Небольшое предложение было бы использовать FrameLayout вместо LinearLayout, но я сомневаюсь, что это имеет значение.
Кайл Айви

43
Если какой-то макет присутствует ниже прокрутки, тогда этот трюк не сработает
Шридху Мадху

2
Можете ли вы предоставить код? это не сработало для меня, так что, возможно, я делаю что-то не так ...
Android-разработчик

не будет работать, если у вас есть кнопки под ним. это будет отталкивать кнопки от экрана
Ли Тянь Гун

43

Это помогло мне настроить его в 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>

Создание пользовательского представления, безусловно, лучший способ достичь этого.
Бертрам Гилфойл

И это работает, если у вас есть макет ниже прокрутки! Отлично!
Ragaisis

25

(Я знаю, что это не дает прямого ответа на вопрос, но может быть полезным для тех, кто ищет функциональность maxHeight)

ConstraintLayout предлагает максимальную высоту для своих детей через

app:layout_constraintHeight_max="300dp"
app:layout_constrainedHeight="true" 

или

app:layout_constraintWidth_max="300dp"
app:layout_constrainedWidth="true" 

Пример использования здесь .


2
Это не работает, если представление находится в вертикальной цепочке (как указано в документах ConstraintLayout)
Arno Schoonbee

Спасибо за информацию.
user1506104

8

Я бы прокомментировал ответ 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 Н.


4

Нет способа установить maxHeight. Но вы можете установить высоту.

Для этого вам понадобится определить высоту каждого элемента вашего 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));

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
Идеальное решение действительно. Умно и просто. Подходит даже для переработчика.
Алекс Белец

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>

2

У меня есть ответ здесь:

https://stackoverflow.com/a/29178364/1148784

Просто создайте новый класс, расширяющий 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);
    }

2

Как упомянуто выше, ConstraintLayout предлагает максимальную высоту для своих детей через:

app:layout_constraintHeight_max="300dp"
app:layout_constrainedHeight="true"

Кроме того, если максимальная высота для одного дочернего элемента ConstraintLayout остается неопределенной до запуска приложения, все еще есть способ заставить этого дочернего элемента автоматически адаптировать изменяемую высоту независимо от того, где он был расположен в вертикальной цепочке.

Например, нам нужно показать нижний диалог с изменяемым заголовком TextView, изменяемым ScrollView и изменяемым нижним колонтитулом TextView. Максимальная высота диалога составляет 320 dp ,, когда общая высота не достигает 320dp. ScrollView действует как wrap_content, когда общая высота превышает ScrollView, действует как «maxHeight = 320dp - высота заголовка - высота нижнего колонтитула».

Мы можем добиться этого только с помощью файла макета 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"

Максимальное использование по горизонтали - то же самое.


1

Вы пытались использовать значение layout_weight ? Если вы установите для него значение больше 0, это растянет это представление на оставшееся свободное место.

Если у вас было несколько видов, которые нужно было растянуть, то значение станет весом между ними.

Таким образом, если бы у двух представлений было установлено значение layout_weight, равное 1, они оба растянулись бы, чтобы заполнить пространство, но оба растянулись бы до равного пространства. Если вы установите один из них в значение 2, то он будет растягиваться вдвое больше, чем другой вид.

Некоторая дополнительная информация здесь перечислена под Линейной Компоновкой.


1
Проблема с весами в том, что они относительны. Различные размеры экрана, разные результаты, например, с экраном 800px, я могу установить вес на определенное значение, так что конкретная область заканчивается желаемыми 200px. Тем не менее, при размере экрана 850 пикселей конкретная область будет больше 200 пикселей, а вложенные элементы по-прежнему будут иметь размер только 200 пикселей. Вот почему я предпочел иметь значение, maxHeightкоторое я могу установить для определенного значения падения. (Я наконец-то закончил вычислять и устанавливать высоты программно)
znq

1

я думаю, вы можете установить heiht во время выполнения для 1 предмета просто scrollView.setHeight(200px), для 2 предметов scrollView.setheight(400px)для 3 или болееscrollView.setHeight(600px)


8
Вы не хотите устанавливать Размер в 'px'. Весьма вероятно, что вы не получите желаемый результат на нескольких устройствах. developer.android.com/guide/topics/resources/…
Хернд Дербелд

@HerndDerBeld, как это работает в коде. если вы хотите, вы всегда можете легко конвертировать из DP в пиксели: float пиксели = TypedValue.applyDimension (TypedValue.COMPLEX_UNIT_DIP, dp, context.getResources () .getDisplayMetrics ());
Android-разработчик

1

Как мы знаем, устройства под управлением Android могут иметь разные размеры экрана. Как мы далее знаем, представления должны динамически корректироваться и становиться подходящим пространством.

Если вы установите максимальную высоту, вы можете заставить вид не получить достаточно места или занять меньше места. Я знаю, что иногда кажется, что практически установить максимальную высоту. Но если разрешение когда-либо резко изменится, и оно изменится !, то представление с максимальной высотой будет выглядеть неуместно.

Я думаю, что нет правильного способа сделать макет, который вы хотите. Я бы порекомендовал вам продумать макет, используя менеджеры макетов и соответствующие механизмы. я не знаю, чего вы пытаетесь достичь, но для меня немного странно, что список должен содержать только три элемента, а затем пользователь должен прокрутить.

Кстати. minHeight не гарантируется (и, возможно, не должно существовать). это может иметь некоторое преимущество, чтобы заставить элементы быть видимыми, в то время как другие относительные элементы становятся меньше.


6
Это не верно. По этой логике вы также не сможете сказать android: layout_width = "500dp".
Гленн Мейнард

Я не понимаю, что должно быть неправильно, ни ваша логическая интерпретация. Если вы установите layout_width (что иногда нормально), это не гарантирует, что вы получите это. 500dp, может быть, на одном устройстве подходит, на другом нет. Менеджеры по раскладке ftw! Может быть, вы могли бы объяснить больше bcs, что беспокоит голосование вниз
Диего Френер

3
Правильно сказать android: maxWidth = "500dp" так же, как сказать android: layout_width = "500dp". Они оба заставляют определенные размеры на представлении.
Гленн Мейнард

1

Если кто-то рассматривает возможность использования точного значения для LayoutParams, например

setLayoutParams(new LayoutParams(Y, X );

Не забудьте принять во внимание плотность отображения устройства, иначе вы можете получить очень странное поведение на разных устройствах. Например:

Display display = getWindowManager().getDefaultDisplay();
DisplayMetrics d = new DisplayMetrics();
display.getMetrics(d);
setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, (int)(50*d.density) ));

0

Сначала получите высоту элемента в пикселях

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

тогда просто

Display display = getWindowManager().getDefaultDisplay();    
DisplayMetrics displayMetrics = new DisplayMetrics();    
display.getMetrics(displayMetrics);    
scrollView.getLayoutParams().height = (int)((heightOfItem * 3)*displayMetrics .density);

0

если вы, ребята, хотите сделать прокрутку без просмотра или просмотр списка без переполнения, просто используйте RelativeLayout с видом сверху и снизу сверху и снизу:

<ScrollView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_above="@+id/topview"
    android:layout_below="@+id/bottomview" >

Это требует большего кредита. Конкретная, но простая реализация.
Люк Эллисон

0

Я использовал собственный ScrollView, сделанный в Kotlin, который использует maxHeight. Пример использования:

<com.antena3.atresplayer.tv.ui.widget.ScrollViewWithMaxHeight
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:maxHeight="100dp">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>
</com.antena3.atresplayer.tv.ui.widget.ScrollViewWithMaxHeight>

Вот код ScrollViewWidthMaxHeight:

import android.content.Context
import android.util.AttributeSet
import android.widget.ScrollView
import timber.log.Timber

class ScrollViewWithMaxHeight @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : ScrollView(context, attrs, defStyleAttr) {

companion object {
    var WITHOUT_MAX_HEIGHT_VALUE = -1
}

private var maxHeight = WITHOUT_MAX_HEIGHT_VALUE

init {
    val a = context.obtainStyledAttributes(
        attrs, R.styleable.ScrollViewWithMaxHeight,
        defStyleAttr, 0
    )
    try {
        maxHeight = a.getDimension(
            R.styleable.ScrollViewWithMaxHeight_android_maxHeight,
            WITHOUT_MAX_HEIGHT_VALUE.toFloat()
        ).toInt()
    } finally {
        a.recycle()
    }
}

override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
    var heightMeasure = heightMeasureSpec
    try {
        var heightSize = MeasureSpec.getSize(heightMeasureSpec)
        if (maxHeight != WITHOUT_MAX_HEIGHT_VALUE) {
            heightSize = maxHeight
            heightMeasure = MeasureSpec.makeMeasureSpec(heightSize, MeasureSpec.AT_MOST)
        } else {
            heightMeasure = MeasureSpec.makeMeasureSpec(heightSize, MeasureSpec.UNSPECIFIED)
        }
        layoutParams.height = heightSize
    } catch (e: Exception) {
        Timber.e(e, "Error forcing height")
    } finally {
        super.onMeasure(widthMeasureSpec, heightMeasure)
    }
}

fun setMaxHeight(maxHeight: Int) {
    this.maxHeight = maxHeight
}
}

который также нуждается в этой декларации в values/attrs.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="ScrollViewWithMaxHeight">
        <attr name="android:maxHeight" />
    </declare-styleable>
</resources>
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.