Limita l'altezza di ListView su Android


92

Voglio mostrare un pulsante sotto un file ListView. Il problema è che se ListViewviene esteso (elementi aggiunti ...), il pulsante viene spinto fuori dallo schermo.

Ho provato un LinearLayoutcon i pesi (come suggerito in Android: perché non c'è maxHeight per una vista? ), Ma o ho sbagliato i pesi o semplicemente non ha funzionato.

Inoltre, ho trovato da qualche parte il suggerimento per utilizzare un file RelativeLayout. Il ListViewverrà quindi impostato sopra il pulsante con il android:layout_aboveparam.

Il problema con questo è che non so come posizionare il pulsante in seguito. Nell'esempio che ho trovato, la Vista sotto è ListViewstata regolata usando android:layout_alignParentBottom, ma non voglio che il mio pulsante si aggrappi alla parte inferiore dello schermo.

Qualche idea a parte l'uso del metodo setHeight e del calcolo dello spazio richiesto?


Modifica: ho molte risposte utili.

  • La soluzione di bigstone e user639183 ha funzionato quasi perfettamente. Tuttavia, ho dovuto aggiungere un'imbottitura / margine extra nella parte inferiore del pulsante, poiché sarebbe stato comunque spinto a metà fuori dallo schermo (ma poi fermato)

  • La risposta di Adinia con il relativo layout va bene solo se vuoi che il pulsante sia fissato nella parte inferiore dello schermo. Non è quello che intendevo, ma potrebbe comunque essere utile per gli altri.

  • La soluzione di AngeloS è stata quella che ho scelto alla fine perché ha creato gli effetti che volevo. Tuttavia, ho apportato due piccole modifiche al LinearLayoutpulsante intorno:

    • Innanzitutto, poiché non volevo avere valori assoluti nel mio layout, sono passato android:layout_height="45px"a wrap_content, che funziona altrettanto bene.

    • In secondo luogo, poiché volevo che il pulsante fosse centrato orizzontalmente, che è supportato solo da verticale LinearLayout, ho cambiato android: orientamento = "orizzontale" in "verticale".

    AngeloS ha anche affermato nel suo post iniziale di non essere sicuro se il android:layout_weight="0.1"param in LinearLayoutgiro ListViewavesse qualche effetto; Ho appena provato e funziona davvero! Senza, il pulsante viene nuovamente spinto fuori dallo schermo.


in questo modo il layout relativo interno sta crescendo più lungo dello schermo, un passaggio potrebbe essere l'aggiunta android:layout_alignParentBottom="true". Ma per essere chiari, vuoi che il pulsante rimanga attaccato alla parte inferiore di ListView quando ci sono pochi elementi? Se sì, guarda cosa dice Rich.
bigstones

sì, è quello che voglio.
medusa

anni dopo, ora abbiamo altezza massima grazie a ConstraintLayout: stackoverflow.com/questions/42694355/...
user1506104

Risposte:


55

Ho avuto questo problema esatto e per risolverlo ne ho creati due separati LinearLayoutsper ospitare rispettivamente ListViewe Button. Da lì ho messo entrambi in un altro Linear Layoute ho impostato quel contenitore android:orientationsu vertical. Ho anche impostato il peso del LinearLayoutche ospitava il ListViewa 0.1ma non so se questo abbia alcun effetto. Da lì, puoi impostare l'altezza del contenitore inferiore (che ha il tuo pulsante) all'altezza che desideri.

EDIT questo è ciò che intendo:

<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 soluzione di cui sopra risolverà il pulsante nella parte inferiore dello schermo.


Per fare in modo che il pulsante rimanga mobile in fondo all'elenco, modificare l'altezza di ListView01in 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>


Lo vedo come qualcosa di simile ai <divtag in HTML .. è solo un wrapper e questa soluzione funziona sicuramente.
AngeloS

E per quanto riguarda l'overhead, non è molto diverso da quello che stai facendo con i layout relativi .. in questo momento hai un wrapper outter e ne stai annidando un altro .. ti sto suggerendo di annidarne solo uno in più e dividere la visualizzazione elenco e il pulsante in due contenitori che creano un effetto pila nel layout lineare verticale. Non posso sottolineare abbastanza che questa è una soluzione funzionante.
AngeloS

1
SÌ! questo funziona! importante da notare, l'unica differenza tra la prima e la seconda soluzione è che il layout lineare deve avvolgere_content, quindi ho finito con un tipo di impostazione iniziale: imgur.com/c9L1Z . Toa chiunque altro trovi questo: la ponderazione è importante, ma può assumere qualsiasi valore.
Sam

2
non c'è bisogno di racchiudere tutte le visualizzazioni in questo modo - controlla la soluzione di @ Sparkle
Richard Le Mesurier

È necessario inserire 45px o un valore esatto per il layout dei pulsanti? Devo invece mettere wrap_content
Mario Velasco

65

Ho risolto questo problema nel codice, impostando l'altezza di listview solo se contiene più di 5 elementi:

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

Si noti che ho impostato l'altezza massima a 5,5 volte l'altezza di un singolo elemento, quindi l'utente saprà per certo che c'è da fare uno scorrimento! :)


1
Bella risposta. Con quel codice scopro l'altezza del mio articolo, e ora posso impostare la dimensione della mia lista per mostrare quanti itens voglio. Grazie.
Derzu

3
Questa è la migliore risposta. Perché scrivere 50 righe di XML nidificato quando è possibile risolvere il problema con 5 righe di codice?
Greg Ennis

Grazie @shaylh. Ha funzionato perfettamente.
Anju

Anche se ho ripulito bene le soluzioni XML, ho comunque finito per utilizzare questa idea nel mio prodotto finale.
Richard Le Mesurier

1
@JayR non è mai troppo tardi per commentare
Manza

21

Sulla tua ultima modifica, devi solo aggiungere anche il android:layout_above="@+id/butt1"tuo codice ListView.

E per mostrare a te (ea tutti quelli qui che hanno provato una risposta in precedenza) che non hai davvero bisogno di usarne più di uno RelativeLayout, ecco il tuo codice corretto :

   <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 il risultato , utilizzando un elenco casuale:
inserisci qui la descrizione dell'immagine


1
Grazie mille per il tuo aiuto, ma avere il pulsante fisso nella parte inferiore dello schermo non era quello che volevo. Dovrebbe essere in fondo a ListView.
medusa

In effetti, sembra che questa volta LinearLayouts possa essere l'unica soluzione per quello che vuoi; Ho provato diverse combinazioni con RelativeLayouts e nessuna ha funzionato davvero :( ... Ma sono contento che tu abbia trovato la tua risposta.
Adinia

1
Grazie! È perfetto!
Guicara

6

Usando LinearLayout, imposta l'altezza del tuo ListViewe Buttonsu wrap_contente aggiungi un peso = 1 a ListView. Questo dovrebbe funzionare come vuoi.
E sì, imposta il layout_gravitydi Buttonabottom


6

Ho trovato un modo per farlo senza utilizzare contenitori annidati, ispirato dalla soluzione di AngeloS.

Ho usato un LinearLayoutcon un orientamento verticale, che ha un ListViewe un pulsante. Ho impostato android:layout_weight="0.1"per il ListView.

Riesce a portare il pulsante stick sempre in fondo alla lista. E il pulsante non viene spinto fuori dallo schermo quando l'elenco cresce.

<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
No, l'ho appena provato e il pulsante rimane in fondo allo schermo, non rimane in fondo all'elenco. :(
Adinia

@Adinia Funziona perfettamente almeno su 4.4.3. Bella soluzione, Sparkle, dovrebbe avere più voti.
Richard Le Mesurier

Humm..forse hai altre impostazioni nel tuo layout? Ho appena provato di nuovo su un Nexus 4 con 4.4.3, e il pulsante rimane nella parte inferiore del layout, non nell'elenco, non si attacca all'elenco.
Adinia

2
@Richard Le Mesurier In effetti, con android:layout_height="wrap_content" per LinearLayout, come nella tua soluzione, funziona perfettamente, e non solo su 4.4.3, ma poiché Sparkle non lo menzionava, l'ho provato match_parentprima.
Adinia

Sto lavorando anche su Pie. +1
Zubair Younas

5

RelativeLayout è una soluzione, se non vuoi che il pulsante rimanga troppo vicino al fondo puoi aggiungere dei margini (riempire un pulsante lo spezzerebbe perché usa uno sfondo a 9 patch e imposta il proprio riempimento).

Sarebbe lo stesso se usassi a LinearLayout: il modo per ottenere ciò che vuoi è impostare l' ListViewaltezza = 0 e il peso = 1, e per il pulsante height = wrap_content e weight = 0. (impostando entrambi su wrap_content, come ha detto user639183, non si limiterà ListViewl'altezza).


Sì, limiterà l' ListViewaltezza di
ernazm

@ user639183 ha appena provato, hai ragione mi dispiace, ne ero così sicuro.
bigstones

Ho cambiato il peso come suggerito (prima avevo ListView weight = 2 e button weight = 1, mi chiedo perché non ha funzionato?) L'effetto è molto interessante: il pulsante ora viene spinto solo a metà dello schermo. In realtà, posso farlo rimanere dove voglio aggiungendo un po 'di "android: layout_marginBottom". Tuttavia, potrei anche provare l'approccio Maxim e vedere se funziona anche questo.
medusa

@jellyfish: non lo so, le mie certezze sono state demolite da @ user639183. L'idea è che l'intera altezza sarà condivisa dalle due viste. La quantità di azioni che ottengono è data dal loro peso, quindi 1-0 -> tutto al primo (2-1 darebbe 1/3 di spazio al secondo). Ho pensato che l'impostazione wrap_content su una visualizzazione a scorrimento lo avrebbe reso alto quanto il suo contenuto. Tuttavia .. hai impostato il layout esterno su fill_parent?
bigstones

È davvero strano che un RelativeLayout annidato (come suggerito da Maxim) non funzioni. Potrei semplicemente postarlo sopra, poiché ho la sensazione di essermi perso qualcosa ...
medusa

2

Probabilmente puoi farlo funzionare usando contenitori nidificati. È possibile che sia necessario avvolgere il Button in un secondo contenitore (interno) in modo che occupi più spazio e allineare il Button alla parte superiore del contenitore interno.

Possibile qualcosa di simile:

RelativeLayout

-- Visualizzazione elenco

- RelativeLayout

---- Pulsante

Utilizzando le diverse opzioni di allineamento sui due contenitori, dovresti essere in grado di farlo funzionare.


Penso che l'utilizzo di un relativeLayout sia la soluzione migliore, ma non riesco davvero a capire perché dovresti usare contenitori nidificati, quando puoi semplicemente usare qualcosa come android:layout_marginTop="30dip" per il pulsante, se vuoi più spazio tra l'elenco e il pulsante, ad esempio.
Adinia

nella domanda, l'OP ha detto che se effettua rigorosamente il posizionamento del pulsante rispetto al ListView, esce dallo schermo una volta che il ListView è sufficientemente alto. In questi casi devi essere complicato con i contenitori nidificati
Rich

2

Questo è il modo più pulito per farlo:

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

È simile alla soluzione di Angelos ma non è necessario il layout lineare che avvolge ListView. Inoltre puoi usare un FrameLayout che è più leggero rispetto a un LinearLayout per concludere il Button.


2

L'idea è delineata nelle ottime soluzioni da:

Entrambi sembrano funzionare perfettamente almeno su v4.4.3.

Dovevo ancora dedicare del tempo alla scrittura del codice di prova per verificarlo e confermare ogni metodo. Di seguito è riportato il codice di test minimo e autonomo richiesto.


Il layout si basa sulla soluzione di Sparkle, poiché contiene un minor numero di layout annidati. Inoltre è stato aggiornato per riflettere i controlli LINT correnti.

<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 fornisce il codice boilerplate per testare personalmente la soluzione.

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

Sentiti libero di aggiungere o migliorare in quanto si tratta di "wiki della comunità".


1

Prova a usare RelativeLayout con il pulsante in basso. Imposta l'altezza del layout come "wrap_content". Non l'ho provato Solo un'idea


Ho pensato davvero che dovrebbe funzionare, ma sembra che non sia così. Il layout riempie tutto lo spazio rimasto, come se l'altezza fosse impostata su match_parent. Non ha senso, però.
medusa

1

in LinearLayout, aggiungi semplicemente android:layout_weight="1"a ListView


0

Di seguito è quello che ha funzionato per me ...

 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 = ... deve essere impostato anche ...

L'esempio sopra è quando usi TableRow e ListView all'interno di TableRow ...


0

avvolgere il contenuto in un layout lineare

nella visualizzazione elenco aggiungi: android: layout_weight = "1"

nel tuo pulsante imposta altezza fissa

<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

Se desideri che il contenuto sotto la visualizzazione elenco si sposti verso il basso man mano che vengono aggiunti elementi all'elenco, prova questa soluzione:

Aggiungi un riempimento positivo nella parte inferiore della visualizzazione elenco e un riempimento negativo nella parte superiore del "piè di pagina". Funzionerà in un layout lineare o in un layout 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"/>

-1

Poiché non esiste un attributo MaxHeight, la soluzione migliore è impostare l'altezza di ListView su un valore impostato o utilizzare wrap_content.


-2

Ho risolto questo problema inserendo ListView in un ScrollView. A Lint non è piaciuto, ma aggiungendo minHeight e impostando fillViewport su true, ho un buon risultato.

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

Ho provato questo approccio, anche se limita la dimensione della listview, non mi permette di scorrere la listview, il che è imbarazzante. Sai perché?
Eduardo

7
NON inserire MAI un ListView in un ScrollView!
jenzz

Sì, non inserire mai un ListView all'interno di un ScrollView. Ma con questo "hack" è possibile ... stackoverflow.com/a/14577399/1255990
Leonardo Cardoso
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.