il metodo getView dell'adattatore listview personalizzato viene chiamato più volte e in nessun ordine coerente


162

Ho un adattatore elenco personalizzato:

class ResultsListAdapter extends ArrayAdapter<RecordItem> {

nel metodo override 'getView' faccio una stampa per verificare quale sia la posizione e se si tratta di convertView oppure no:

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        System.out.println("getView " + position + " " + convertView);

L'output di questo (quando l'elenco viene visualizzato per la prima volta, nessun input dell'utente ancora)

04-11 16:24:05.860: INFO/System.out(681): getView 0 null  
04-11 16:24:29.020: INFO/System.out(681): getView 1 android.widget.RelativeLayout@43d415d8  
04-11 16:25:48.070: INFO/System.out(681): getView 2 android.widget.RelativeLayout@43d415d8  
04-11 16:25:49.110: INFO/System.out(681): getView 3 android.widget.RelativeLayout@43d415d8  
04-11 16:25:49.710: INFO/System.out(681): getView 0 android.widget.RelativeLayout@43d415d8  
04-11 16:25:50.251: INFO/System.out(681): getView 1 null  
04-11 16:26:01.300: INFO/System.out(681): getView 2 null  
04-11 16:26:02.020: INFO/System.out(681): getView 3 null  
04-11 16:28:28.091: INFO/System.out(681): getView 0 null  
04-11 16:37:46.180: INFO/System.out(681): getView 1 android.widget.RelativeLayout@43cff8f0  
04-11 16:37:47.091: INFO/System.out(681): getView 2 android.widget.RelativeLayout@43cff8f0  
04-11 16:37:47.730: INFO/System.out(681): getView 3 android.widget.RelativeLayout@43cff8f0  

AFAIK, anche se non l'ho trovato esplicitamente, getView () viene chiamato solo per le righe visibili. Poiché la mia app inizia con quattro righe visibili, almeno i numeri di posizione che vanno da 0 a 3 hanno un senso. Ma il resto è un casino:

  • Perché viene chiamato getview per ogni riga tre volte?
  • Da dove vengono queste convertView viste quando non ho ancora fatto scorrere?

Ho fatto un po 'di ricerca e, senza ottenere una buona risposta, ho notato che le persone associavano questo problema a problemi di layout. Quindi, nel caso, ecco il layout che contiene l'elenco:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_height="fill_parent"
    android:layout_width="fill_parent" 
    android:orientation="vertical" >

    <TextView android:id="@+id/pageDetails"
        android:layout_width="fill_parent" 
        android:layout_height="wrap_content" />

    <ListView android:id="@+id/list"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" 
        android:drawSelectorOnTop="false" />

</LinearLayout>

e il layout di ogni singola riga:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="108dp"    
android:padding="4dp">

<ImageView
    android:id="@+id/thumb"        
    android:layout_width="120dp"
    android:layout_height="fill_parent"        
    android:layout_alignParentTop="true"
    android:layout_alignParentBottom="true"
    android:layout_alignParentLeft="true"
    android:layout_marginRight="8dp"        
    android:src="@drawable/loading" />

<TextView  
    android:id="@+id/price"
    android:layout_width="wrap_content"
    android:layout_height="18dp"         
    android:layout_toRightOf="@id/thumb"
    android:layout_alignParentBottom="true"       
    android:singleLine="true" />

<TextView  
    android:id="@+id/date"
    android:layout_width="wrap_content"
    android:layout_height="18dp"         
    android:layout_alignParentBottom="true"
    android:layout_alignParentRight="true" 
    android:paddingRight="4dp"       
    android:singleLine="true" />

<TextView
    android:id="@+id/title"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:textSize="17dp" 
    android:layout_toRightOf="@id/thumb"
    android:layout_alignParentRight="true"
    android:layout_alignParentTop="true"
    android:paddingRight="4dp"   
    android:layout_alignWithParentIfMissing="true"
    android:gravity="center" />

</RelativeLayout>

Grazie per il tuo tempo

Risposte:


271

Questo non è un problema, non c'è assolutamente alcuna garanzia sull'ordine in cui getView()verrà chiamato né quante volte. Nel tuo caso particolare stai facendo la cosa peggiore possibile con a ListViewdandogli a height=wrap_content. Questo costringe ListViewa misurare alcuni bambini fuori dall'adattatore al momento del layout, per sapere quanto dovrebbe essere grande. Questo è ciò che fornisce ListViewil passaggio convertViewsche vedi passato getView()anche prima dello scorrimento.


10
Non lo trovo una buona ragione. Va bene se vuoi essere in grado di cambiare, ma al momento non risponde perché sta facendo questo comportamento?
cdpnet,

45
@cdpnet Ha detto perché lo stava facendo, il tuo ListViewha un'altezza di wrap_content, quindi non è sicuro della sua altezza, quindi espone alcuni bambini come una specie di tester per vedere cosa si adatta. Cambia il tuo ListViewper fill_parentpoi controllare i tuoi registri le chiamate dovrebbero essere drasticamente ridotte.
Blundell,

4
Il comportamento tra ListView nell'era 2.3 e 4.x è cambiato in modo significativo. Il take away è che, come è stato progettato ListView, le tue celle / list_item devono essere viste pure e devono essere ottimizzate per disegnare rapidamente. Secondo la conferenza I / O di Google su ListView, in cui Romain ha presentato, getView potrebbe essere chiamato solo per ottimizzare il rendering della vista e scartare il risultato. Anche se secondo me non è così facile per gli sviluppatori come iOS UITableView, è così.
Cameron Lowell Palmer,

4
Grazie mille! Non ho capito perché getView () sia chiamato così spesso. Ho spostato wrap_content su fill_parent e ora la mia app è di nuovo veloce :)
Julia Hexen,

28
Dovrebbe esserci un avviso quando si imposta ListViews su layout_height = wrap_content perché causa così tanti problemi di prestazioni.
nathanielwolf,

54

Prova con match_parentsulla layout_heightproprietà della vista elenco. Impedirà getView()di essere chiamato così spesso.


4
Nota che Fred T specifica il layout_height di LIST VIEW ... non la riga, che è quello che ho provato più volte
mblackwell8

8
Si noti che fill_parentdovrebbe essere sostituito con match_parentper API livello 8 e superiore.
Jason Axelson,

Penso che il metodo getView () chiamerà più volte, impostando larghezza e altezza come match_parent per ListView risolve solo il problema delle prestazioni.
Cuong Vo

45

Mi sono sbarazzato di questo problema quando ho cambiato layout_width e layout_height in match_parent (cambiare solo layout_height non ha aiutato).


Nota utile fai attenzione se hai oggetti nidificati. Devi cambiare quello "più alto" in match_parent . Spero che aiuti qualcuno.


amico di una parola "fantastico" ma non so perché wrap_content sta creando problemi. Questo ha risolto il mio problema.
Android Killer,

@AndroidKiller quando lo usi wrap_content, quindi ListViewnon sai quanta voce dell'elenco è lì per diventare il contenuto dell'elenco, ecco perché ListViewcerca di creare quante righe ritiene sia adatta visualizzare, e quando usi fill_parento match_parent, ListViewpensi, va bene, la mia altezza è xe ho bisogno del nnumero di righe da mostrare.
Adil Soomro,

Grazie mille ! Con questo comportamento le immagini stavano cambiando in modo casuale a causa di più chiamate ... Ora questo è ok dall'inizio.
Chostakovitch,

7

Non sono in grado di rispondere alla tua domanda "Perché", ma ho sicuramente una soluzione al problema dell'irritante " ListView che si ripetono " (se hai oggetti nella tua raccolta che sono più dell'altezza dello schermo).

Come molte persone di cui sopra sono menzionati, mantenere il Android: layout_height proprietà della ListVew tag come fill_parent .

E per quanto riguarda la funzione getView (), la soluzione è utilizzare una classe statica chiamata ViewHolder . Dai un'occhiata a questo esempio. Svolge con successo il compito di aggiungere tutti gli elementi in ur Array o ArrayCollection.

Spero che questo aiuti gli amici !!

Cordiali saluti, Siddhant


Sii cauto sull'uso del modello ViewHolder nella prima versione di Android (pre ICS) perché può portare a un'eccezione MemoryLeak. sentiti libero di usarlo nelle versioni ICS +.
Vahid Ghadiri,

@Siddhant, grazie, passo un giorno per capire il motivo per cui ripeto le immagini GridViewe le cambioandroid:layout_height e android:layout_widthnon funziona per me. Ma usando ViewHolderimmagini fisse ripetute per me ho seguito questo tutorial android-vogue.blogspot.com/2011/06/…
Nikolay Podolnyy

4

Ques: Perché Adapter chiama getView () molte volte? Risp: Mentre Listview esegue il rendering allo scorrimento, si aggiorna è la vista con le prossime viste imminenti, per cui l'adattatore deve ottenere viste chiamando getView ().

Ques: Perché le chiamate sono inferiori se la larghezza e l'altezza di listview sono impostate su fill_parent? Risp: Perché poiché il gonfiatore ha le dimensioni fisse per l'area dello schermo per l'elenco, calcola una volta per il rendering delle viste sullo schermo.

Spero che risolverà la tua domanda.


Ottima spiegazione Ma match_parent potrebbe essere una scelta migliore.
Fattie,

Sì @JoeBlow, si consiglia di utilizzare match_parent anziché fill_parent dall'API 2.4 / 3.0 in poi
jitain sharma,

4

Stavo riscontrando lo stesso problema con il menu a discesa in AutoCompleteTextView. Ho combattuto con il problema per due giorni fino al mio arrivo qui e tu mi mostri la soluzione.

Se scrivo dropDownHeight = "match_parent" il problema è risolto. Ora il problema è correlato all'interfaccia utente (quando hai un elemento il menu a discesa è troppo grande) ma il problema di più chiamate (molto più importante) è stato risolto.

Grazie!!


2

"Perché viene chiamato getview per ogni riga tre volte?" Perché getView viene chiamato quando scorri su listview e per dirlo meglio che viene chiamato quando la posizione di una vista del tuo elenco viene modificata!


ok, ma la vista non è cambiata. ci sono quattro visualizzazioni di riga sullo schermo quando inizia l'attività, e senza scorrimento o nessun input dell'utente ricevo tre chiamate getView per ogni riga ...
edzillion

Questa risposta è sbagliata La risposta corretta è che l'utilizzo di ** wrap_content ** provoca getView in un numero enorme di chiamate.
Fattie,

2

Sto avendo lo stesso problema. Se l'altezza è impostata su fill_parent, ricevo "normalmente" 2 chiamate per riga. Ma, se imposto l'altezza del mio ListView sul valore esatto, diciamo a 300dp, ricevo esattamente una chiamata GetView per riga.

Quindi, mi sembra che l'unico modo sia determinare prima l'altezza dello schermo, quindi impostare programmaticamente l'altezza di listvilew su quel valore. Non mi piace Spero che ci sia un modo migliore.


1
Grazie per quello È divertente - questa domanda è rimasta inattiva per mesi ormai solo per avere alcuni commenti visualizzati nelle ultime settimane. Non sto più facendo questo progetto ma dato che ho un po 'di tempo, lo proverò e poi torno qui per confermare. grazie ancora.
edzillion

lo stesso qui, ricevo due chiamate per riga. Righe 0,1,2,3 e poi 100 ms dopo un altro 0,1,2,3 - che è un dolore nel culo quando si raccolgono le statistiche su quali file vengono visualizzate
Someone Somewhere

2

Per tutti voi che ancora (Dopo aver impostato il heightla ListViewamatch_parent ) sono bloccati (come me):

È inoltre necessario impostare il heightlayout principale su match_parent.

Vedi esempio sotto. Il LinearLayoutgenitore è qui:

<LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@string/something" />

        <ListView
            android:id="@+id/friendsList"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </LinearLayout>

1

Questo potrebbe arrivare tardi, ma se stai usando layout_weightricordati di impostare semprelayout_width="0dp"


0

ho fatto questa soluzione, forse non è la migliore, ma fa il lavoro ...

//initialize control string
private String control_input = " ";

allora =

@Override
public View getView(int position, View convertView, ViewGroup parent) {

    View gridview = convertView;

    // change input_array for the array you use
    if (!control_input.equals(input_array[position])) {
        control_input = input_array[position];

        //do your magic

    } 

    return gridview;
}

spero che sia d'aiuto!

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.