Limitar la altura de ListView en Android

92

Quiero mostrar un botón debajo de ListView. El problema es que, si ListViewse amplía (elementos agregados ...), el botón se saca de la pantalla.

Probé una LinearLayoutcon pesas (como se sugiere en Android:? Por qué no hay maxHeight de Vista ), pero tampoco tengo los pesos mal o simplemente no funcionó.

Además, encontré en alguna parte la sugerencia para usar un RelativeLayout. A ListViewcontinuación, se establecería encima del botón con el android:layout_aboveparámetro.

El problema con esto es que no sé cómo colocar el botón después. En el ejemplo que encontré, la Vista debajo de ListViewse ajustó usando android:layout_alignParentBottom, pero no quiero que mi botón se adhiera a la parte inferior de la pantalla.

¿Alguna idea además de usar el método setHeight y algunos cálculos del espacio requerido?


Editar: tengo muchas respuestas útiles.

  • La solución de bigstone y user639183 casi funcionó perfectamente. Sin embargo, tuve que agregar un margen / relleno adicional en la parte inferior del botón, ya que aún se empujaría a la mitad de la pantalla (pero luego se detendría)

  • La respuesta de Adinia con el diseño relativo solo está bien si desea que el botón esté fijo en la parte inferior de la pantalla. No es lo que pretendía, pero aún podría ser útil para otros.

  • La solución de AngeloS fue la que elegí al final, ya que creó los efectos que quería. Sin embargo, hice dos cambios menores LinearLayoutalrededor del botón:

    • Primero, como no quería tener ningún valor absoluto en mi diseño, cambié android:layout_height="45px"a wrap_content, que también funciona bien.

    • En segundo lugar, como quería que el botón estuviera centrado horizontalmente, que solo es compatible con vertical LinearLayout, cambié android: Orientación = "horizontal" a "vertical".

    AngeloS también declaró en su publicación inicial que no estaba seguro de si el android:layout_weight="0.1"parámetro en el LinearLayoutentorno ListViewtuvo algún efecto; ¡Lo intenté y realmente lo hace! Sin, el botón vuelve a salir de la pantalla.

Medusa
fuente
de esta manera, el diseño relativo interno se hace más largo que la pantalla, se podría agregar un paso android:layout_alignParentBottom="true". Pero para ser claros, ¿desea que el botón permanezca adjunto a la parte inferior de ListView cuando hay pocos elementos? Si es así, vea lo que dice Rich.
bigstones
sí, eso es lo que quiero.
medusa
años después, ahora tenemos la altura máxima gracias a ConstraintLayout: stackoverflow.com/questions/42694355/…
user1506104

Respuestas:

55

Tuve este problema exacto y para resolverlo creé dos separados LinearLayoutspara albergar el ListViewy Buttonrespectivamente. A partir de ahí, puse ambos en otro Linear Layouty puse ese contenedor android:orientationen vertical. También configuré el peso del LinearLayoutque albergaba el ListViewa, 0.1pero no sé si eso tiene algún efecto. Desde allí, puede establecer la altura del contenedor inferior (que tiene su botón) a la altura que desee.

EDITAR esto es lo que quiero decir:

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

La solución anterior arreglará el botón en la parte inferior de la pantalla.


Para que el botón flote al final de la lista, cambie la altura de ListView01a 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
fuente
Lo veo como algo similar a las <divetiquetas en HTML ... es solo un contenedor y esta solución definitivamente funciona.
AngeloS
Y con respecto a la sobrecarga, no es muy diferente de lo que estás haciendo con los diseños relativos ... ahora mismo tienes un contenedor externo y estás anidando otro ... te sugiero que anides solo uno más y dividas la vista de lista y el botón en dos contenedores creando un efecto de pila en el diseño lineal vertical. No puedo enfatizar lo suficiente que esta es una solución de trabajo.
AngeloS
1
¡SI! ¡esto funciona! Es importante tener en cuenta que la única diferencia entre su primera y segunda solución es que linearlayout necesita wrap_content, así que terminé con una configuración de tipo de inicio: imgur.com/c9L1Z . Para cualquiera que encuentre esto: la ponderación es importante, pero puede tomar cualquier valor.
Sam
2
no es necesario envolver todas las vistas de esa manera; consulte la solución de @ Sparkle
Richard Le Mesurier
¿Es necesario poner 45px o un valor exacto para el diseño del botón? Necesito poner wrap_content en su lugar
Mario Velasco
65

Resolví este problema en el código, estableciendo la altura de la vista de lista solo si tiene más de 5 elementos:

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

Tenga en cuenta que configuré la altura máxima en 5.5 veces la altura de un solo elemento, por lo que el usuario sabrá con certeza que hay algo de desplazamiento que hacer. :)

shaylh
fuente
1
Buena respuesta. Con ese código, descubro la altura de mi artículo, y ahora puedo establecer el tamaño de mi lista para mostrar cuántos elementos quiero. Gracias.
Derzu
3
Esta es la mejor respuesta. ¿Por qué escribir 50 líneas de XML anidado cuando puede resolver el problema con 5 líneas de código?
Greg Ennis
Gracias @shaylh. Funcionó perfectamente.
Anju
A pesar de que limpié las soluciones XML muy bien, terminé usando esta idea en mi producto final.
Richard Le Mesurier
1
@JayR nunca es demasiado tarde para comentar
Manza
21

En su última edición, solo tiene que agregar también android:layout_above="@+id/butt1"su código ListView.

Y para mostrarte (y a todos los que intentaron una respuesta antes) que realmente no necesitas usar más de uno RelativeLayout, aquí tienes tu código corregido :

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

y el resultado , usando alguna lista aleatoria:
ingrese la descripción de la imagen aquí

Adinia
fuente
1
Muchas, muchas gracias por su ayuda, pero tener el botón fijo en la parte inferior de la pantalla no era lo que pretendía. Se supone que debe estar en la parte inferior de ListView.
medusa
De hecho, parece que esta vez LinearLayouts podría ser la única solución para lo que desea; Probé diferentes combinaciones con RelativeLayouts, y ninguna funcionó realmente :( ... Pero me alegra que hayas encontrado tu respuesta.
Adinia
1
¡Gracias! ¡Es perfecto!
Guicara
6

Usando LinearLayout, establezca la altura de su ListViewy Buttonay wrap_contentagregue un peso = 1 a ListView. Esto debería funcionar como quieras.
Y sí, configure el layout_gravitydel Buttonabottom

ernazm
fuente
6

Encontré una manera de hacer esto sin usar contenedores anidados, inspirada en la solución de AngeloS.

Usé un LinearLayoutcon orientación vertical, que tiene un ListViewy un botón. Configuré el android:layout_weight="0.1"para el ListView.

Se las arregla para que el botón se pegue siempre al final de la lista. Y el botón no sale de la pantalla cuando la lista crece.

<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"
    />
Brillar
fuente
2
No, solo probé y el botón permanece en la parte inferior de la pantalla, no se queda al final de la lista. :(
Adinia
@Adinia Esto funciona perfectamente al menos en 4.4.3. Buena solución, Sparkle, debería tener más votos.
Richard Le Mesurier
Humm ... ¿tal vez tienes otras configuraciones en tu diseño? Acabo de probar nuevamente en un Nexus 4 con 4.4.3, y el botón permanece en la parte inferior del diseño, no en la lista, no se pega a la lista.
Adinia
2
@Richard Le Mesurier De hecho, con android:layout_height="wrap_content" LinearLayout, como en su solución, está funcionando perfectamente, y no solo en 4.4.3, pero como Sparkle no lo mencionó, lo probé match_parentantes.
Adinia
Trabajando en Pie también. +1
Zubair Younas
5

RelativeLayout es una solución, si no desea que el botón se quede demasiado cerca del fondo, puede agregar márgenes (rellenar un botón lo rompería porque usa un fondo de 9 parches y establece su propio relleno).

Sería lo mismo si usara a LinearLayout: la forma de lograr lo que desea es establecer el ListViewen altura = 0 y peso = 1, y para el botón altura = contenido_envío y peso = 0. (establecer ambos en wrap_content, como dijo user639183, no limitaría ListViewla altura).

piedras grandes
fuente
Sí, limitará la ListViewaltura del '
ernazm
@ user639183 acaba de intentarlo, tienes razón, lo siento, estaba tan seguro de eso.
bigstones
Cambié el peso como se sugirió (antes, tenía el peso de ListView = 2 y el peso del botón = 1, me pregunto por qué esto no funcionó). El efecto es muy interesante: el botón ahora solo está presionado hasta la mitad de la pantalla. De hecho, puedo conseguir que se quede donde quiero añadiendo algo de "android: layout_marginBottom". Sin embargo, también podría probar el enfoque de Maxims y ver si eso también funciona.
medusa
@jellyfish: No lo sé, mis certezas fueron demolidas por @ user639183. La idea es que las dos vistas compartan toda la altura. La cantidad de acciones que obtienen viene dada por su peso, por lo que 1-0 -> todo al primero (2-1 daría 1/3 del espacio al segundo). Pensé que establecer wrap_content en una vista de desplazamiento lo haría tan alto como su contenido. Sin embargo ... ¿configuró el diseño exterior en fill_parent?
bigstones
Es realmente extraño que un RelativeLayout anidado (como lo sugiere Maxim) no funcione. Podría publicarlo arriba, ya que tengo la sensación de que me perdí algo ...
medusa
2

Probablemente pueda hacer que esto funcione utilizando contenedores anidados. Es posible que deba envolver el botón en un segundo contenedor (interior) para que ocupe más espacio y simplemente alinear el botón con la parte superior del contenedor interior.

Posible algo como esto:

Disposición relativa

-- Vista de la lista

-- Disposición relativa

---- Botón

Usando las diferentes opciones de alineación en los dos contenedores, debería poder hacer que esto funcione.

Rico
fuente
Creo que usar un formato relativo es la mejor solución, pero realmente no puedo ver por qué usarías contenedores anidados, cuando puedes usar algo como android:layout_marginTop="30dip" para el Botón, si quieres más espacio entre la Lista y el Botón, por ejemplo.
Adinia
en la pregunta, el OP dijo que si él hace estrictamente la posición del botón en relación con ListView, desaparece de la pantalla una vez que ListView crece lo suficiente. En ese tipo de casos, debe complicarse con los contenedores anidados
Rich
2

Esta es la forma más limpia de hacerlo:

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

Es similar a la solución de Angelos pero no necesita el diseño lineal que envuelve ListView. También puede usar un FrameLayout que es más ligero en comparación con un LinearLayout para envolver el Button.

usuario2994360
fuente
2

La idea se describe en las muy buenas soluciones por:

Ambos parecen funcionar perfectamente al menos en v4.4.3.

Todavía tuve que pasar algo de tiempo escribiendo código de prueba para verificarlo y confirmar cada método. A continuación se muestra el código de prueba mínimo y autónomo requerido.


El diseño se basa en la solución de Sparkle, ya que contiene menos diseños anidados. También se ha actualizado para reflejar las comprobaciones actuales de 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 proporciona el código repetitivo para probar la solución por sí mismo.

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

Siéntase libre de agregar o mejorar, ya que esta es una "wiki comunitaria".

Richard Le Mesurier
fuente
1

Intente usar RelativeLayout con el botón en la parte inferior. Establezca la altura del diseño como "wrap_content". No lo he probado. Solo idea

Máxima
fuente
Realmente pensé que esto debería funcionar, pero parece que no. El diseño ocupa todo el espacio que queda, como si la altura estuviera configurada en match_parent. Sin embargo, no tiene sentido.
medusa
1

en LinearLayout, solo agréguelo android:layout_weight="1"a ListView

DawnYu
fuente
0

Lo siguiente es lo que funcionó para mí ...

 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: Params.width = ... Debe configurarse también ...

El ejemplo anterior es cuando usa TableRow y ListView dentro de TableRow ...

Atul Dravid - Pvt blanco. Limitado.
fuente
0

envolver contenido en diseño lineal

en Vista de lista agregar: android: layout_weight = "1"

en tu botón establece altura fija

<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>
Toma
fuente
0

Si desea que el contenido debajo de la vista de lista se mueva hacia abajo a medida que se agregan elementos a la lista, pruebe esta solución:

Agregue relleno positivo en la parte inferior de la vista de lista y relleno negativo en la parte superior del "pie de página". Esto funcionará en un diseño lineal o 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
fuente
-1

Dado que no hay ningún atributo MaxHeight, lo mejor que puede hacer es establecer la altura de ListView en un valor establecido o usar wrap_content.

Chris Cashwell
fuente
-2

Resolví esto colocando ListView dentro de ScrollView. A Lint no le gustó, pero agregando un minHeight y configurando fillViewport en verdadero, tengo un buen 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
fuente
Probé este enfoque, aunque limita el tamaño de la vista de lista, no me permite desplazarme por la vista de lista, lo cual es incómodo. ¿Sabes por qué?
Eduardo
7
¡NUNCA ponga un ListView dentro de un ScrollView!
jenzz
Sí, nunca coloque un ListView dentro de un ScrollView. Pero con este "truco" es posible ... stackoverflow.com/a/14577399/1255990
Leonardo Cardoso