Android: por que não existe maxHeight para uma visualização?

184

As visualizações têm um,minHeight mas de alguma forma estão faltando um maxHeight:

O que estou tentando conseguir é ter alguns itens (visualizações) preenchendo a ScrollView. Quando houver 1..3 itens, quero exibi-los diretamente. Ou seja, ScrollViewtem a altura de 1, 2 ou 3 itens.

Quando houver 4 ou mais itens, quero ScrollViewque pare de expandir (portanto, a maxHeight) e comece a fornecer rolagem.

No entanto, infelizmente não há como definir a maxHeight. Portanto, eu provavelmente tenho que definir minha ScrollViewaltura programaticamente para WRAP_CONTENTquando houver 1..3 itens e definir a altura para 3*sizeOf(View)quando houver 4 ou mais itens.

Alguém pode explicar por que não há maxHeightfornecido, quando já existe um minHeight?

(BTW: alguns pontos de vista, como ImageViewter uma maxHeightimplementado.)

znq
fonte
Eu
publiquei
2
Eu criei uma solução para isso em chintanrathod.com/…
Chintan Rathod
A remoção do maxHeight do Google é irritante e inconveniente. Veja todas as coisas que você precisa fazer agora para obter o mesmo resultado.
23617 Ian Wambai

Respostas:

86

Nenhuma dessas soluções funcionou para o que eu precisava: um ScrollView definido como wrap_content, mas com um maxHeight, para que ele parasse de se expandir após um determinado ponto e iniciasse a rolagem. Eu simplesmente substituí o método onMeasure no ScrollView.

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

Isso pode não funcionar em todas as situações, mas certamente me fornece os resultados necessários para o meu layout. E também aborda o comentário de madhu.

Se algum layout estiver presente abaixo da visualização de rolagem, esse truque não funcionará - madhu 5 de março às 4:36

zunir
fonte
1
Quando isso não funcionaria? Parece uma solução muito limpa. Me faz pensar por que eles não o implementaram no xml.
Christophe Smet
1
Provavelmente não funcionaria se você definisse manualmente a altura para dizer 240. Não me preocupei em verificar o modo em que a visualização de rolagem está, porque eu só precisava oferecer suporte a 1 modo específico (wrap_content).
whizzle
Há uma edição para esta resposta que diz 'Se algum layout estiver presente abaixo da visualização de rolagem, esse truque não funcionará', o que não é verdade. Eu tenho um layout alinhado à parte inferior da página e este layout personalizado tem "android: layout_above = '+ ...'", que coloca sempre o layout personalizado acima do layout de fundo :)
Máquina Tribe
70

Para criar um ScrollViewou ListViewcom um maxHeight, você só precisa criar um LinearLayout transparente ao redor dele, com uma altura do que você deseja que o maxHeight seja. Você então define a Altura do ScrollView como wrap_content. Isso cria um ScrollView que parece crescer até que sua altura seja igual ao LinearLayout pai.

JustinMorris
fonte
3
Isso foi útil para impedir que uma caixa de diálogo com o conteúdo da rolagem se expanda até a altura total da tela.
Kyle Ivey
Uma pequena sugestão seria usar um FrameLayout em vez de um LinearLayout, mas duvido que isso importe.
Kyle Ivey
43
Se algum esquema presente abaixo do scrollview então este truque não vai funcionar
Sreedhu Madhu
2
você pode fornecer algum código? não funcionou para mim, então talvez eu esteja fazendo algo errado ...
Android developer
não funcionará se você tiver botões abaixo dele. ele vai apertar os botões fora da tela
Li Tian Gong
43

Isso funcionou para mim para torná-lo personalizável em 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>

layout de exemplo

<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>
SlickDev
fonte
Criar uma exibição personalizada é definitivamente a melhor maneira de conseguir isso.
Bertram Gilfoyle
E isso funciona se você tiver o layout abaixo da visualização de rolagem! Perfeito!
Ragaisis
25

(Eu sei que isso não responde diretamente à pergunta, mas pode ser útil para outras pessoas que procuram a funcionalidade maxHeight)

O ConstraintLayout oferece altura máxima para seus filhos via

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

ou

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

Exemplo de uso aqui .

user1506104
fonte
2
Isso não funciona se a exibição está em uma cadeia vertical (conforme especificado na documentação ConstraintLayout)
Arno Schoonbee
Obrigado pela informação.
user1506104
8

Eu teria comentado a resposta da whizzle se pudesse, mas achei útil observar que, para resolver esse problema no contexto do modo de várias janelas no Android N, precisava alterar um pouco o código para isso:

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

Isso permite que o redimensionamento do layout seja menor que a altura máxima, mas também impede que ele seja maior que a altura máxima. Eu usei esta é uma classe de layout que Substitui RelativeLayoute isso me permitiu criar uma caixa de diálogo personalizada com um ScrollViewfilho MaxHeightRelativeLayoutque não expande toda a altura da tela e também diminui para caber no menor tamanho de viúva em várias janelas para Android N.

JP Krause
fonte
4

Não há como definir maxHeight. Mas você pode definir a altura.

Para fazer isso, você precisará descobrir a altura de cada item do scrollView. Depois disso, basta definir sua altura scrollView como numberOfItens * heightOfItem.

Para descobrir a altura de um item, faça o seguinte:

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

Para definir a altura, faça o seguinte:

// 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));
Derzu
fonte
4

Envolva seu ScrollViewem torno de sua uma planície LinearLayoutcom layout_height = "max_height", isso vai fazer um trabalho perfeito. Na verdade, eu tenho esse código em produção nos últimos 5 anos, com zero problemas.

<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>
Sai
fonte
1
Solução perfeita, de fato. Inteligente e simples. É adequado mesmo para visualização de reciclagem.
Alex Belets
3

Minha MaxHeightScrollViewvisualização personalizada

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>

Usando

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

    ...

</....MaxHeightScrollView>
Phan Van Linh
fonte
2

Eu tenho uma resposta aqui:

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

Basta criar uma nova classe estendendo o ScrollView e substituir seu onMeasuremétodo.

    @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);
    }
babay
fonte
2

Como mencionado acima, o ConstraintLayout oferece altura máxima para seus filhos via:

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

Além disso, se a altura máxima para um filho de um ConstraintLayout for incerta até a execução do aplicativo, ainda há uma maneira de fazer com que esse filho adapte automaticamente uma altura mutável, independentemente de onde foi colocada na cadeia vertical.

Por exemplo, precisamos mostrar uma caixa de diálogo inferior com um cabeçalho mutável TextView, um ScrollView mutável e um rodapé mutável TextView. A altura máxima da caixa de diálogo é 320dp , quando a altura total não atinge 320dp, o ScrollView atua como wrap_content, quando a altura total excede o ScrollView, atua como "maxHeight = 320dp - altura do cabeçalho - altura do rodapé".

Podemos conseguir isso apenas através do arquivo de layout 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>

A maioria dos códigos de importação é curta:

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

app:layout_constrainedHeight="true"

O uso horizontal de maxWidth é o mesmo.

xionghg
fonte
1

Você já tentou usar o valor layout_weight ? Se você defini-lo como um valor maior que 0, ele expandirá essa visualização para o espaço restante disponível.

Se você teve várias visualizações que precisavam ser ampliadas, o valor se tornará um peso entre elas.

Portanto, se você tivesse duas visualizações definidas para um valor de layout_weight de 1, elas esticariam para preencher o espaço, mas estenderiam para uma quantidade igual de espaço. Se você definir um deles com o valor 2, ele aumentará duas vezes mais que a outra visualização.

Mais algumas informações aqui listadas em Layout linear.

Bryan Denny
fonte
1
O problema com os pesos é que eles são relativos. Diferentes tamanhos de tela, resultados diferentes, por exemplo, com uma tela de 800 px, posso definir um peso para um determinado valor, de modo que uma área específica termine com os 200 px desejados. No entanto, com uma tela de 850px, a área específica acaba sendo maior que 200px, mas os elementos incluídos ainda são apenas 200px. É por isso que eu preferi ter um maxHeightque eu possa definir para um valor de mergulho específico. (I finalmente terminou para calcular e ajustar as alturas de programação)
znq
1

Eu acho que você pode definir a altura em tempo de execução para 1 item apenas scrollView.setHeight(200px), para 2 itens scrollView.setheight(400px)para 3 ou maisscrollView.setHeight(600px)

Yayo28
fonte
8
Você não deseja definir a dimensão em 'px'. É muito provável que você não obtenha o resultado desejado em vários dispositivos. developer.android.com/guide/topics/resources/...
Hernd DerBeld
@HerndDerBeld é assim que funciona no código. se desejar, você sempre pode converter de DP em pixels com bastante facilidade: pixels flutuantes = TypedValue.applyDimension (TypedValue.COMPLEX_UNIT_DIP, dp, context.getResources () .getDisplayMetrics ());
developer android
1

Como sabemos, os dispositivos com Android podem ter diferentes tamanhos de tela. Conforme sabemos, as visualizações devem se ajustar dinamicamente e se tornar o espaço apropriado.

Se você definir uma altura máxima, talvez force a vista a não ter espaço suficiente ou ocupar menos espaço. Eu sei que, às vezes, parece praticamente definir uma altura máxima. Mas se a resolução mudar drasticamente e mudar !, a exibição, que tem uma altura máxima, parecerá inadequada.

Eu acho que não há maneira adequada de fazer exatamente o layout que você deseja. eu recomendo que você pense sobre seu layout usando gerenciadores de layout e mecanismos relativos. Não sei o que você está tentando alcançar, mas me parece um pouco estranho que uma lista mostre apenas três itens e o usuário precise rolar a tela.

btw. minHeight não é garantido (e talvez também não deva existir). pode ter algum benefício em forçar a visibilidade dos itens, enquanto outros itens relativos ficam menores.

Diego Frehner
fonte
6
Isto está errado. Por essa lógica, você também não poderia dizer android: layout_width = "500dp".
Glenn Maynard
Não vejo o que deve estar errado nem a sua interpretação lógica. Se você definir layout_width (que às vezes está ok), não será garantido que você entenda. 500dp talvez procure em um dispositivo apropriado, em outro não. Gerentes de layout ftw! Talvez você poderia explicar mais BCS os incomoda baixo voto :)
Diego Frehner
3
É tão válido dizer android: maxWidth = "500dp" quanto dizer android: layout_width = "500dp". Ambos forçam dimensões específicas na vista.
Glenn Maynard
1

Se alguém estiver pensando em usar o valor exato para LayoutParams, por exemplo,

setLayoutParams(new LayoutParams(Y, X );

Lembre-se de levar em consideração a densidade da tela do dispositivo, caso contrário, poderá ocorrer um comportamento muito estranho em diferentes dispositivos. Por exemplo:

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

Primeiro obtenha a altura do item em pixels

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

então simplesmente

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

se vocês quiserem criar uma visualização de rolagem ou de lista sem transbordamento, mas em um RelativeLayout com uma visualização superior e inferior na parte superior e inferior:

<ScrollView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_above="@+id/topview"
    android:layout_below="@+id/bottomview" >
Um Quoc Duy Nguyen
fonte
Isso precisa de mais crédito. Implementação específica, mas simples.
Luke Allison
0

Eu usei um ScrollView personalizado feito no Kotlin que usa maxHeight. Exemplo de uso:

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

Aqui está o código de 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
}
}

que também precisa desta declaração em values/attrs.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="ScrollViewWithMaxHeight">
        <attr name="android:maxHeight" />
    </declare-styleable>
</resources>
Mario Velasco
fonte