Limiter la hauteur de ListView sur Android


92

Je veux afficher un bouton sous un ListView. Le problème est que si le ListViewfichier est étendu (éléments ajoutés ...), le bouton est poussé hors de l'écran.

J'ai essayé un LinearLayoutavec des poids (comme suggéré dans Android: pourquoi n'y a-t-il pas de maxHeight pour une vue? ), Mais soit je me suis trompé de poids, soit cela n'a tout simplement pas fonctionné.

Aussi, j'ai trouvé quelque part le conseil d'utiliser un fichier RelativeLayout. Le ListViewserait alors placé au-dessus du bouton avec le android:layout_aboveparamètre.

Le problème avec ceci est que je ne sais pas comment positionner le bouton par la suite. Dans l'exemple que j'ai trouvé, la vue ci-dessous a ListViewété ajustée à l'aide de android:layout_alignParentBottom, mais je ne veux pas que mon bouton s'accroche au bas de l'écran.

Des idées en dehors de l'utilisation de la méthode setHeight et du calcul de l'espace requis?


Edit: j'ai beaucoup de réponses utiles.

  • La solution de bigstone & user639183 a presque parfaitement fonctionné. Cependant, j'ai dû ajouter un rembourrage / une marge supplémentaire au bas du bouton, car il serait toujours poussé à mi-chemin de l'écran (mais ensuite arrêté)

  • La réponse d'Adinia avec la disposition relative ne convient que si vous voulez que le bouton soit fixé au bas de l'écran. Ce n'est pas ce que je voulais mais cela pourrait quand même être utile pour les autres.

  • La solution d'AngeloS a été celle que j'ai choisie à la fin car elle ne faisait que créer les effets que je voulais. Cependant, j'ai apporté deux modifications mineures au LinearLayoutcontour du bouton:

    • Tout d'abord, comme je ne voulais pas avoir de valeurs absolues dans ma mise en page, je suis passé android:layout_height="45px"à wrap_content, ce qui fonctionne très bien aussi.

    • Deuxièmement, comme je voulais que le bouton soit centré horizontalement, ce qui n'est supporté que par vertical LinearLayout, j'ai changé android: orientation = "horizontal" en "vertical".

    AngeloS a également déclaré dans son message initial qu'il n'était pas sûr que le android:layout_weight="0.1"paramètre LinearLayoutautour de l ' ListViewait eu un effet; J'ai juste essayé et c'est le cas! Sans, le bouton est à nouveau poussé hors de l'écran.


de cette façon, la disposition relative intérieure devient plus longue que l'écran, une étape pourrait être l'ajout android:layout_alignParentBottom="true". Mais pour être clair, voulez-vous que le bouton reste attaché au bas de la ListView lorsqu'il y a peu d'éléments? Si oui, voyez ce que dit Rich.
bigstones

oui, c'est ce que je veux.
méduses

ans après, nous avons maintenant une hauteur maximale grâce à ConstraintLayout: stackoverflow.com/questions/42694355
...

Réponses:


55

J'ai eu ce problème exact et pour le résoudre, j'ai créé deux séparés LinearLayoutspour héberger le ListViewet Buttonrespectivement. De là, j'ai mis les deux dans un autre Linear Layoutet mis ce conteneur android:orientationà vertical. J'ai également défini le poids du LinearLayoutqui abritait le ListViewto 0.1mais je ne sais pas si cela a un effet. À partir de là, vous pouvez définir la hauteur du conteneur inférieur (qui a votre bouton) à la hauteur que vous souhaitez.

EDIT c'est ce que je veux dire:

<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 solution ci-dessus fixera le bouton en bas de l'écran.


Pour que le bouton flotte en bas de la liste, changez la hauteur de ListView01en 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>


Je le vois comme quelque chose de similaire aux <divbalises HTML ... c'est juste un wrapper et cette solution fonctionne définitivement.
AngeloS

Et en ce qui concerne les frais généraux, ce n'est pas très différent de ce que vous faites avec les mises en page relatives ... pour le moment, vous avez un emballage extérieur et vous en imbriquez un autre ... je vous suggère d'en imbriquer un de plus et de diviser la vue de liste et le bouton dans deux conteneurs créant un effet de pile dans la disposition linéaire verticale. Je ne saurais trop insister sur le fait qu’il s’agit d’une solution efficace.
AngeloS

1
OUI! cela marche! Il est important de noter que la seule différence entre votre première et votre deuxième solution est que la mise en page linéaire doit wrap_content, donc je me suis retrouvé avec un type de création configuré: imgur.com/c9L1Z . À toute autre personne qui trouve ceci: la pondération est importante, mais peut prendre n'importe quelle valeur.
Sam

2
pas besoin d'emballer toutes les vues comme ça - consultez la solution de @ Sparkle
Richard Le Mesurier

Est-il nécessaire de mettre 45px ou une valeur exacte pour la disposition des boutons? Je dois mettre wrap_content à la place
Mario Velasco

65

J'ai résolu ce problème dans le code, en définissant la hauteur de la liste uniquement si elle contient plus de 5 éléments:

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

Notez que j'ai défini la hauteur maximale à 5,5 fois la hauteur d'un seul élément, de sorte que l'utilisateur saura avec certitude qu'il y a du défilement à faire! :)


1
Bonne réponse. Avec ce code, je découvre la hauteur de mon article, et maintenant je peux définir la taille de ma liste pour montrer combien d'itens je veux. Merci.
Derzu

3
C'est la meilleure réponse. Pourquoi écrire 50 lignes de XML imbriqué alors que vous pouvez résoudre le problème avec 5 lignes de code?
Greg Ennis

Merci @shaylh. A parfaitement fonctionné.
Anju

Même si j'ai bien nettoyé les solutions XML, j'ai quand même fini par utiliser cette idée dans mon produit final.
Richard Le Mesurier

1
@JayR il n'est jamais trop tard pour commenter
Manza

21

Lors de votre dernière modification, il vous suffit d'ajouter également android:layout_above="@+id/butt1"votre code ListView.

Et pour vous montrer (ainsi qu'à tous ceux qui ont essayé une réponse plus tôt) que vous n'avez vraiment pas besoin d'en utiliser plus d'une RelativeLayout, voici votre code corrigé :

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

et le résultat , en utilisant une liste aléatoire:
entrez la description de l'image ici


1
Merci beaucoup pour votre aide, mais avoir le bouton fixé en bas de l'écran n'était tout simplement pas ce que je voulais. Il est censé être au bas de la ListView.
méduses

En effet, semble cette fois LinearLayouts pourrait être la seule solution pour ce que vous voulez; J'ai essayé différentes combinaisons avec RelativeLayouts, et aucune n'a vraiment fonctionné: (... Mais je suis content que vous ayez trouvé votre réponse.
Adinia

1
Je vous remercie! C'est parfait!
Guicara

6

À l'aide de LinearLayout, définissez la hauteur de votre ListViewet Buttonà wrap_contentet ajoutez un poids = 1 à ListView. Cela devrait fonctionner comme vous le souhaitez.
Et oui, réglez layout_gravityle Buttonsurbottom


6

Nous avons trouvé un moyen de le faire sans utiliser de conteneurs imbriqués, inspirés de la solution AngeloS.

J'ai utilisé un LinearLayoutavec une orientation verticale, qui a un ListViewet un bouton. J'ai mis le android:layout_weight="0.1"pour le ListView.

Il parvient à toujours placer le bouton en bas de la liste. Et le bouton n'est pas poussé hors de l'écran lorsque la liste s'allonge.

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

2
Non, il suffit de l'essayer et le bouton reste en bas de l'écran, il ne reste pas en bas de la liste. :(
Adinia

@Adinia Cela fonctionne parfaitement au moins sur 4.4.3. Belle solution, Sparkle, devrait avoir plus de votes.
Richard Le Mesurier

Humm..peut-être avez-vous d'autres paramètres dans votre mise en page? Je viens de tester à nouveau sur un Nexus 4 avec 4.4.3, et le bouton reste en bas de la mise en page, pas dans la liste, il ne colle pas à la liste.
Adinia

2
@Richard Le Mesurier En effet, avec android:layout_height="wrap_content" pour le LinearLayout, comme dans votre solution, cela fonctionne parfaitement, et pas seulement sur 4.4.3, mais comme Sparkle ne l'a pas mentionné, je l'ai essayé avec match_parentavant.
Adinia

Travailler sur Pie aussi. +1
Zubair Younas

5

RelativeLayout est une solution, si vous ne voulez pas que le bouton reste trop près du bas, vous pouvez ajouter des marges (le remplissage d'un bouton le casserait car il utilise un arrière-plan à 9 patchs et définit son propre remplissage).

Ce serait la même chose si vous utilisiez a LinearLayout: la façon d'obtenir ce que vous voulez est de définir le ListViewsur une hauteur = 0 et un poids = 1, et pour le bouton height = wrap_content et weight = 0. (définir les deux sur wrap_content, comme l'a dit user639183, ne limiterait pas ListViewla hauteur de).


Oui, cela limitera la ListViewhauteur du
ernazm

@ user639183 vient d'essayer, vous avez raison Je suis désolé, j'en étais tellement sûr.
bigstones

J'ai changé le poids comme suggéré (avant, j'avais le poids ListView = 2 et le poids du bouton = 1, je me demande pourquoi cela n'a pas fonctionné?) L'effet est très intéressant: le bouton n'est maintenant poussé qu'à mi-chemin de l'écran. En fait, je peux le faire rester là où je le veux en ajoutant un "android: layout_marginBottom". Cependant, je pourrais aussi essayer l'approche Maxims et voir si cela fonctionne aussi.
méduses

@jellyfish: Je ne sais pas, mes certitudes ont été démolies par @ user639183. L'idée est que toute la hauteur sera partagée par les deux vues. Le nombre d'actions qu'ils obtiennent est donné par leur poids, donc 1-0 -> tout pour le premier (2-1 donnerait 1/3 d'espace au second). Je pensais que définir wrap_content sur une vue défilante le rendrait aussi haut que son contenu. Cependant .. avez-vous défini la disposition extérieure sur fill_parent?
bigstones

C'est vraiment bizarre qu'un RelativeLayout imbriqué (comme suggéré par Maxim) ne fonctionne pas. Je pourrais simplement le poster ci-dessus, car j'ai le sentiment que je viens de manquer quelque chose ...
méduse

2

Vous pouvez probablement faire cela en utilisant des conteneurs imbriqués. Il est possible que vous deviez envelopper le Button dans un deuxième conteneur (interne) afin qu'il prenne plus d'espace, et alignez simplement le Button sur le haut du conteneur interne.

Quelque chose comme ça possible:

Disposition relative

- ListView

-- Disposition relative

---- Bouton

En utilisant les différentes options d'alignement sur les deux conteneurs, vous devriez être en mesure de faire fonctionner cela.


Je pense que l'utilisation d'un relativeLayout est la meilleure solution, mais je ne vois vraiment pas pourquoi vous utiliseriez des conteneurs imbriqués, alors que vous pouvez simplement utiliser quelque chose comme android:layout_marginTop="30dip" pour le bouton, si vous voulez plus d'espace entre la liste et le bouton, par exemple.
Adinia

dans la question, l'OP a dit que s'il fait strictement le positionnement du bouton par rapport au ListView, il sort de l'écran une fois que le ListView devient suffisamment grand. Dans ce genre de cas, vous devez devenir délicat avec les conteneurs imbriqués
Rich

2

C'est la façon la plus propre de le faire:

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

C'est similaire à la solution d'Angelos mais vous n'avez pas besoin de la disposition linéaire qui encapsule la ListView. Vous pouvez également utiliser un FrameLayout qui est plus léger qu'un LinearLayout pour boucler le Button.


2

L'idée est esquissée dans les très bonnes solutions par:

Ils semblent tous les deux fonctionner parfaitement au moins sur la v4.4.3.

Je devais encore passer du temps à écrire le code de test pour le vérifier et confirmer chaque méthode. Vous trouverez ci-dessous le code de test minimal et autonome requis.


La mise en page est basée sur la solution de Sparkle, car elle contient moins de mise en page imbriquée. A également été mis à jour pour refléter les contrôles LINT actuels.

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

L'activité fournit le code standard pour tester la solution par vous-même.

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

N'hésitez pas à ajouter ou à améliorer car il s'agit d'un "wiki communautaire".


1

Essayez d'utiliser RelativeLayout avec le bouton en bas. Définissez la hauteur de la mise en page sur "wrap_content". Je n'ai pas essayé. Juste une idée


J'ai vraiment pensé que cela devrait fonctionner, mais il semble que non. La mise en page remplit tout l'espace restant, comme si la hauteur était définie sur match_parent. Cela n'a pas de sens, cependant.
méduses

1

dans LinearLayout, ajoutez simplement android:layout_weight="1"à votre ListView


0

Voici ce qui a fonctionné pour moi ...

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


                }

Remarque: Le paramètre params.width = ... doit également être défini ...

L'exemple ci-dessus est lorsque vous utilisez TableRow et ListView dans TableRow ...


0

envelopper le contenu dans une mise en page linéaire

dans la vue Liste, ajoutez: android: layout_weight = "1"

dans votre jeu de boutons hauteur fixe

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

0

Si vous souhaitez que le contenu sous la vue de liste se déplace vers le bas à mesure que des éléments sont ajoutés à la liste, essayez cette solution:

Ajoutez un remplissage positif en bas de la vue de liste et un remplissage négatif en haut du "pied de page". Cela fonctionnera dans une disposition linéaire ou une disposition relative.

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

-1

Puisqu'il n'y a pas d'attribut MaxHeight, votre meilleur pari est de définir la hauteur de ListView sur une valeur définie, ou d'utiliser wrap_content.


-2

J'ai résolu ce problème en plaçant le ListView dans un ScrollView. Lint n'a pas aimé, mais en ajoutant un minHeight et en définissant le fillViewport sur true, j'ai un bon résultat.

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

J'ai essayé cette approche, même si elle limite la taille de la listview, elle ne me laisse pas faire défiler la listview, ce qui est gênant. Est-ce que tu sais pourquoi?
Eduardo

7
NE JAMAIS mettre de ListView dans un ScrollView!
jenzz

Oui, ne mettez jamais de ListView dans un ScrollView. Mais avec ce "hack" c'est possible ... stackoverflow.com/a/14577399/1255990
Leonardo Cardoso
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.