Limite de altura de ListView no Android

92

Quero mostrar um botão sob a ListView. O problema é que, se o ListViewfor estendido (itens adicionados ...), o botão será empurrado para fora da tela.

Eu tentei um LinearLayoutcom pesos (como sugerido no Android: por que não há maxHeight para um View? ), Mas ou achei os pesos errados ou simplesmente não funcionou.

Além disso, encontrei em algum lugar a dica para usar a RelativeLayout. O ListViewseria então definido acima do botão com o android:layout_aboveparâmetro.

O problema com isso é que não sei como posicionar o botão depois. No exemplo que encontrei, a visualização abaixo do ListViewfoi ajustada usando android:layout_alignParentBottom, mas não quero que meu botão grude na parte inferior da tela.

Alguma ideia além de usar o método setHeight e alguns cálculos do espaço necessário?


Edit: recebi muitas respostas úteis.

  • A solução da bigstone's & user639183 quase funcionou perfeitamente. No entanto, eu tive que adicionar um preenchimento / margem extra na parte inferior do botão, já que ele ainda seria empurrado para fora da tela (mas depois parado)

  • A resposta de Adinia com o layout relativo só é adequada se você quiser que o botão seja fixado na parte inferior da tela. Não é o que eu pretendia, mas ainda pode ser útil para outras pessoas.

  • A solução da AngeloS foi a que escolhi no final, pois ela acabou de criar os efeitos que eu queria. No entanto, fiz duas pequenas alterações ao LinearLayoutredor do botão:

    • Primeiro, como eu não queria ter nenhum valor absoluto em meu layout, mudei android:layout_height="45px"para wrap_content, o que também funciona bem.

    • Em segundo lugar, como eu queria que o botão ficasse centralizado horizontalmente, o que só é suportado pela vertical LinearLayout, alterei android :idance = "horizontal" para "vertical".

    AngeloS também afirmou em sua postagem inicial que não tinha certeza se o android:layout_weight="0.1"parâmetro em LinearLayouttorno do ListViewteve algum efeito; Eu apenas tentei e realmente funciona! Sem, o botão é empurrado para fora da tela novamente.

medusa
fonte
desta forma, o layout relativo interno está crescendo mais que a tela, uma etapa pode ser adicionar android:layout_alignParentBottom="true". Mas, para ser claro, você deseja que o botão permaneça anexado à parte inferior do ListView quando houver poucos itens? Se sim, veja o que diz Rich.
bigstones
sim, é isso que eu quero.
água
anos depois, agora temos altura máxima graças ao ConstraintLayout: stackoverflow.com/questions/42694355/…
user1506104

Respostas:

55

Eu tive esse problema exato e para resolvê-lo criei dois separados LinearLayoutspara abrigar o ListViewe Buttonrespectivamente. A partir daí, coloco os dois em outro Linear Layoute configurei o contêiner android:orientationpara vertical. Eu também definir o peso do o LinearLayoutque abrigava a ListViewpara 0.1, mas não sei se isso tem algum efeito. A partir daí, você pode definir a altura do recipiente inferior (que contém o seu botão) para qualquer altura que desejar.

EDITAR isto é o que quero dizer:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">

<LinearLayout
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_weight="0.1"
    android:orientation="horizontal">

    <ListView
        android:id="@+id/ListView01"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:dividerHeight="2px"></ListView>
</LinearLayout>

<LinearLayout
    android:layout_width="fill_parent"
    android:layout_height="45px"
    android:background="@drawable/drawable"
    android:orientation="horizontal">

    <Button
        android:id="@+id/moreButton"
        android:layout_width="wrap_content"
        android:layout_height="fill_parent"
        android:layout_alignParentRight="true"
        android:background="@drawable/btn_more2"
        android:paddingRight="20px" />

</LinearLayout>

A solução acima corrigirá o botão na parte inferior da tela.


Para que o botão flutue na parte inferior da lista, altere a altura de ListView01para wrap_content:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical">

<LinearLayout
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_weight="0.1"
    android:orientation="horizontal">

    <ListView
        android:id="@+id/ListView01"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:dividerHeight="2px"></ListView>
</LinearLayout>

<LinearLayout
    android:layout_width="fill_parent"
    android:layout_height="45px"
    android:background="@drawable/drawable"
    android:orientation="horizontal">

    <Button
        android:id="@+id/moreButton"
        android:layout_width="wrap_content"
        android:layout_height="fill_parent"
        android:layout_alignParentRight="true"
        android:background="@drawable/btn_more2"
        android:paddingRight="20px" />
</LinearLayout>

AngeloS
fonte
Eu vejo isso como algo semelhante a <divtags em HTML .. é apenas um invólucro e essa solução definitivamente funciona.
AngeloS
E com relação à sobrecarga, não é muito diferente do que você está fazendo com os layouts relativos .. agora você tem um invólucro externo e está aninhando outro .. estou sugerindo que aninhe apenas mais um e divida a exibição de lista e o botão em dois contêineres, criando um efeito de pilha no layout linear vertical. Eu não posso enfatizar o suficiente que esta é uma solução de trabalho.
AngeloS
1
SIM! isso funciona! importante observar, a única diferença entre a primeira e a segunda solução é que o linearlayout precisa para wrap_content, então terminei com um tipo de iniciação configurado: imgur.com/c9L1Z . Para qualquer pessoa que descubra isso: a ponderação é importante, mas pode assumir qualquer valor.
Sam
2
não há necessidade de envolver todas as visualizações assim - confira a solução do @ Sparkle
Richard Le Mesurier
É necessário colocar 45px ou um valor exato para o layout do botão? Preciso colocar wrap_content em vez disso
Mario Velasco
65

Resolvi esse problema no código, definindo a altura do listview apenas se ele contiver mais de 5 itens:

if(adapter.getCount() > 5){
        View item = adapter.getView(0, null, listView);
        item.measure(0, 0);         
        ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, (int) (5.5 * item.getMeasuredHeight()));
        listView.setLayoutParams(params);
}

Observe que eu defino a altura máxima para 5,5 vezes a altura de um único item, então o usuário saberá com certeza que há alguma rolagem a ser feita! :)

Shaylh
fonte
1
Boa resposta. Com esse código descobri a altura do meu item, e agora posso definir o tamanho da minha lista para mostrar quantos itens quero. Obrigado.
Derzu de
3
Esta é a melhor resposta. Por que escrever 50 linhas de XML aninhado quando você pode resolver o problema com 5 linhas de código?
Greg Ennis
Obrigado @shaylh. Funcionou perfeitamente.
Anju
Mesmo tendo limpado as soluções XML muito bem, ainda acabei usando essa ideia em meu produto final.
Richard Le Mesurier de
1
@JayR nunca é tarde para comentar
Manza
21

Em sua última edição, você só precisa adicionar também android:layout_above="@+id/butt1"em seu código ListView.

E para mostrar a você (e a todos aqui que tentaram uma resposta anteriormente) que você realmente não precisa usar mais de um RelativeLayout, aqui está seu código corrigido :

   <RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="@drawable/bkg">
    <TextView
        android:id="@+id/textView1"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="TextView1"
        android:layout_alignParentTop="true">
    </TextView>
    <TextView
        android:id="@+id/textView2"
        android:layout_below="@+id/textView1"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="TextView2"
        android:layout_centerHorizontal="true">
    </TextView>
    <Button
        android:id="@+id/butt1"
        android:text="string/string3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_alignParentBottom="true">
    </Button>
    <ListView
        android:id="@+id/android:list"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="2dip"
        android:layout_marginBottom="2dip"
        android:drawSelectorOnTop="false"
        android:visibility="visible"
        android:layout_below="@+id/textView2"
        android:layout_above="@+id/butt1" />
    </RelativeLayout>

e o resultado , usando alguma lista aleatória:
insira a descrição da imagem aqui

Adinia
fonte
1
Muito, muito obrigado por sua ajuda, mas ter o botão fixado na parte inferior da tela não era o que eu pretendia. Ele deveria estar na parte inferior do ListView.
água
Na verdade, parece que desta vez LinearLayouts pode ser a única solução para o que você deseja; Eu tentei diferentes combinações com RelativeLayouts, e nenhuma funcionou :( ... Mas estou feliz que você encontrou sua resposta.
Adinia,
1
Obrigado! Está perfeito!
Guicara
6

Usando LinearLayout, defina a altura de seu ListViewe Buttonpara wrap_contente adicione um peso = 1 a ListView. Isso deve funcionar como você deseja.
E sim, defina o layout_gravityde Buttonparabottom

ernazm
fonte
6

Encontrou uma maneira de fazer isso sem usar contêineres aninhados, inspirado na solução da AngeloS.

Usei um LinearLayoutcom orientação vertical, que tem um ListViewe um botão. Eu defino o android:layout_weight="0.1"para o ListView.

Consegue colocar o botão sempre no final da lista. E o botão não é empurrado para fora da tela quando a lista aumenta.

<ListView
    android:id="@+id/notes_list"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_weight="0.1"        
    />

<Button
    android:id="@+id/create_note_btn"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"        
    android:onClick="onCreateButtonClicked"
    android:text="@string/create_notes"
    />
Brilhar
fonte
2
Não, apenas tentei e o botão fica na parte inferior da tela, não fica na parte inferior da lista. :(
Adinia
@Adinia Isso funciona perfeitamente pelo menos no 4.4.3. Boa solução, Sparkle, deve ter mais votos.
Richard Le Mesurier de
Humm ... talvez você tenha algumas outras configurações em seu layout? Acabei de testar novamente em um Nexus 4 com 4.4.3, e o botão fica na parte inferior do layout, não na lista, ele não adere à lista.
Adinia
2
@Richard Le Mesurier De fato, com android:layout_height="wrap_content" o LinearLayout, como na sua solução, está funcionando perfeitamente, e não apenas no 4.4.3, mas como o Sparkle não mencionou, eu tentei match_parentantes.
Adinia
Trabalhando no Pie também. +1
Zubair Younas
5

RelativeLayout é uma solução, se você não quiser que o botão fique muito perto da parte inferior, você pode adicionar margens (o preenchimento de um botão iria quebrá-lo porque ele usa um fundo de 9 remendos e define seu próprio preenchimento).

Seria o mesmo se você usasse a LinearLayout: a maneira de conseguir o que deseja é definir o ListViewpara a altura = 0 e peso = 1, e para o botão altura = wrap_content e peso = 0. (definir ambos como wrap_content, como o usuário639183 disse, não limitaria ListViewa altura de).

bigstones
fonte
Sim, vai limitar a ListViewaltura de
ernazm
@ user639183 acabou de tentar, você está certo. Sinto muito, eu tinha tanta certeza disso.
bigstones
Mudei o peso conforme sugerido (antes, eu tinha o peso do ListView = 2 e o peso do botão = 1, me pergunto por que isso não funcionou?) O efeito é muito interessante: o botão agora é empurrado apenas até a metade da tela. Na verdade, posso fazê-lo ficar onde quero adicionando alguns "android: layout_marginBottom". No entanto, também posso tentar a abordagem do Maxims e ver se está funcionando também.
água
@ água-viva: Não sei, minhas certezas foram demolidas por @ user639183. A ideia é que toda a altura seja compartilhada pelas duas visualizações. A quantidade de compartilhamentos que eles obtêm é dada por seu peso, então 1-0 -> tudo para o primeiro (2-1 daria 1/3 de espaço para o segundo). Achei que definir wrap_content em uma exibição de rolagem o tornaria tão alto quanto seu conteúdo. No entanto ... você definiu o layout externo para fill_parent?
bigstones
É realmente estranho que um RelativeLayout aninhado (como sugerido por Maxim) não funcione. Eu poderia postar acima, pois tenho a sensação de que perdi algo ...
água
2

Você provavelmente pode fazer isso funcionar usando contêineres aninhados. É possível que você precise envolver o botão em um segundo contêiner (interno) para que ele ocupe mais espaço e apenas alinhar o botão ao topo do contêiner interno.

Possível algo assim:

Esquema relativo

-- Exibição de lista

-- Esquema relativo

---- Botão

Usando as diferentes opções de alinhamento nos dois contêineres, você deve conseguir fazer isso funcionar.

Rico
fonte
Eu acho que usar um relativeLayout é a melhor solução, mas eu realmente não consigo ver por que você usaria containers aninhados, quando você pode apenas usar algo como android:layout_marginTop="30dip" para o Botão, se quiser mais espaço entre a Lista e o Botão, por exemplo.
Adinia,
na pergunta, o OP disse que se ele fizer estritamente o posicionamento do botão em relação ao ListView, ele sai da tela assim que o ListView crescer o suficiente. Nesses tipos de casos, você precisa ser complicado com contêineres aninhados
Rich
2

Esta é a maneira mais limpa de fazer isso:

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">
  <ListView
      android:id="@+id/ListView01"
      android:layout_width="fill_parent"
      android:layout_height="0dp"
      android:layout_weight="1"
      android:dividerHeight="2px">
  </ListView>
  <FrameLayout
      android:layout_width="fill_parent"
      android:layout_height="wrap_content"
      android:background="@drawable/drawable"
      >
    <Button android:background="@drawable/btn_more2"
        android:id="@+id/moreButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:paddingRight="20px"
        />    
  </LinearLayout>
</LinearLayout>

É semelhante à solução de Angelos, mas você não precisa do linearlayout que envolve o ListView. Você também pode usar um FrameLayout que é mais leve do que um LinearLayout para encerrar o Botão.

user2994360
fonte
2

A ideia é delineada nas soluções muito boas por:

Ambos parecem funcionar perfeitamente pelo menos na v4.4.3.

Eu ainda tive que gastar algum tempo escrevendo o código de teste para verificar e confirmar cada método. Abaixo está o código de teste mínimo necessário e independente.


O layout é baseado na solução do Sparkle, pois contém menos layout aninhado. Também foi atualizado para refletir as verificações atuais do LINT.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    tools:context="MainActivity" >

    <ListView
        android:id="@+id/listview"
        android:layout_width="fill_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />

    <Button
        android:id="@+id/btn"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="Grow List"
        tools:ignore="HardcodedText" />

</LinearLayout>

Activity fornece o código padrão para testar a solução por si mesmo.

public class MainActivity extends Activity
{
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        final ListView listview = (ListView)findViewById(R.id.listview);
        final ArrayAdapter<String> adapter = 
                new ArrayAdapter<String>(this,
                                         android.R.layout.simple_list_item_1,
                                         new ArrayList<String>());
        adapter.setNotifyOnChange(true);
        listview.setAdapter(adapter);

        findViewById(R.id.btn).setOnClickListener(new View.OnClickListener()
        {

            @Override
            public void onClick(View v)
            {
                int count = adapter.getCount();
                adapter.add(String.valueOf(count));
                listview.setSelection(count);
            }
        });
    }
}

Sinta-se à vontade para adicionar ou melhorar, pois este é um "wiki da comunidade".

Richard Le Mesurier
fonte
1

Tente usar o RelativeLayout com o botão na parte inferior. Defina a altura do layout como "wrap_content". Eu não tentei. Apenas ideia

Máxima
fonte
Eu realmente pensei que isso deveria funcionar, mas parece que não. O layout preenche todo o espaço restante, como se a altura fosse definida como match_parent. Mas não faz sentido.
água
1

no LinearLayout, basta adicionar android:layout_weight="1"a você ListView

DawnYu
fonte
0

A seguir está o que funcionou para mim ...

 if(adapter.getCount() > 3){
                    View item = adapter.getView(0, null, impDateListView);
                    item.measure(0, 0);         
                    LayoutParams params = impDateListView.getLayoutParams();
                    params.width = LayoutParams.MATCH_PARENT;
                    params.height = 3 * item.getMeasuredHeight();
                    impDateListView.setLayoutParams(params);


                }

Nota: O params.width = ... Precisa ser definido também ...

O exemplo acima é quando você usa TableRow e ListView dentro de TableRow ...

Atul Dravid - Branco Unip. Ltd.
fonte
0

envolver o conteúdo em layout linear

na visualização de lista, adicione: android: layout_weight = "1"

em seu botão definir altura fixa

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:orientation="vertical">

<ListView
    android:id="@+id/my_list"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_weight="1">
</ListView>

 <Button
    android:text="Button"
    android:layout_width="match_parent"
    android:layout_height="50dp"/>

</LinearLayout>
A MA
fonte
0

Se você deseja que o conteúdo abaixo da visualização de lista desça conforme os elementos são adicionados à lista, tente esta solução:

Adicione preenchimento positivo na parte inferior da visualização de lista e preenchimento negativo na parte superior do "rodapé". Isso funcionará em um layout linear ou relativo.

<ListView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:paddingBottom="50dp"/>

<TextView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginTop="-50dp"/>
Jenny Pushkarskaya
fonte
-1

Como não há atributo MaxHeight, sua melhor aposta é definir a altura do ListView para um valor definido ou usar wrap_content.

Chris Cashwell
fonte
-2

Resolvi isso colocando o ListView dentro de um ScrollView. Lint não gostou, mas adicionando minHeight e definindo fillViewport como true, obtive um bom resultado.

    <ScrollView 
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="1dp"
        android:layout_marginRight="1dp"
        android:minHeight="100dp"
        android:fillViewport="true"
        android:fadeScrollbars="false"
        android:paddingTop="2dp" 
        android:orientation="vertical"
        android:scrollbarAlwaysDrawVerticalTrack="true" >

        <ListView
            android:id="@+id/workoutAElistRoutines"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="fill_vertical"
            android:background="@drawable/dialog_inner_border_tan"
            android:choiceMode="singleChoice"
            android:orientation="vertical"
            android:paddingLeft="3dp"
            android:paddingRight="3dp" >

        </ListView>
    </ScrollView>        
Jon
fonte
Eu tentei essa abordagem, embora ela limite o tamanho do listview, não me deixa rolar o listview, o que é estranho. Você sabe por quê?
Eduardo
7
NUNCA coloque um ListView dentro de um ScrollView!
jenzz
Sim, nunca coloque um ListView dentro de um ScrollView. Mas com este "hack" é possível ... stackoverflow.com/a/14577399/1255990
Leonardo Cardoso