Der benutzerdefinierte Listenansicht-Adapter getView wird mehrmals und in keiner zusammenhängenden Reihenfolge aufgerufen

162

Ich habe einen benutzerdefinierten Listenadapter:

class ResultsListAdapter extends ArrayAdapter<RecordItem> {

Bei der überschriebenen 'getView'-Methode drucke ich, um zu überprüfen, welche Position sich befindet und ob es sich um eine convertView handelt oder nicht:

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

Die Ausgabe davon (wenn die Liste zum ersten Mal angezeigt wird, noch keine Benutzereingabe)

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, obwohl ich es nicht explizit finden konnte, wird getView () nur für sichtbare Zeilen aufgerufen. Da meine App mit vier sichtbaren Zeilen beginnt, sind mindestens die Positionsnummern von 0-3 sinnvoll. Aber der Rest ist ein Chaos:

  • Warum wird getview für jede Zeile dreimal aufgerufen?
  • Woher kommen diese convertViews, wenn ich noch nicht gescrollt habe?

Ich habe ein bisschen nachgeforscht und ohne eine gute Antwort zu bekommen, habe ich bemerkt, dass Leute dieses Problem mit Layoutproblemen in Verbindung bringen. Für den Fall, hier ist das Layout, das die Liste enthält:

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

und das Layout jeder einzelnen Zeile:

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

Vielen Dank für Ihre Zeit

Edzillion
quelle

Antworten:

271

Dies ist kein Problem, es gibt absolut keine Garantie für die Reihenfolge, in der getView()und wie oft angerufen wird. In Ihrem speziellen Fall tun Sie das Schlimmste, was mit a möglich ist, ListViewindem Sie ihm a geben height=wrap_content. Dies zwingt ListViewdazu, zum Zeitpunkt des Layouts einige Kinder aus dem Adapter zu messen, um zu wissen, wie groß er sein sollte. Dies ist, was ListViewmit dem, was convertViewsSie sehen, übergeben wird, getView()noch bevor Sie scrollen.

Romain Guy
quelle
10
Ich finde das keinen guten Grund. Das ist in Ordnung, wenn Sie in der Lage sein möchten, sich zu ändern, aber derzeit antwortet es nicht, warum es dieses Verhalten tut?
cdpnet
45
@cdpnet Er hat gesagt, warum es das getan hat, Sie ListViewhaben eine Größe von wrap_content, daher ist es nicht sicher, wie hoch es ist, daher werden einige Kinder als eine Art Tester eingesetzt, um zu sehen, was hineinpasst. Ändern Sie Ihre, ListViewum fill_parentdann Ihre Protokolle zu überprüfen Die Anrufe sollten drastisch reduziert werden.
Blundell
4
Das Verhalten zwischen ListView der 2.3 und 4.x-Ära hat sich erheblich geändert. Beim Mitnehmen von ListView müssen Ihre Zellen / Listenelemente reine Ansichten sein und für ein schnelles Zeichnen optimiert werden. Laut dem Google I / O-Vortrag in ListView, in dem Romain vorgestellt wurde, wird getView möglicherweise nur aufgerufen, um das Rendern von Ansichten zu optimieren und das Ergebnis zu verwerfen. Meiner Meinung nach ist dies nicht so entwicklerfreundlich wie iOS UITableView, aber so ist es.
Cameron Lowell Palmer
4
Ich danke dir sehr! Ich habe nicht verstanden, warum getView () so oft aufgerufen wird. Ich habe wrap_content auf fill_parent umgestellt und jetzt ist meine App wieder schnell :)
Julia Hexen
28
Es sollte eine Warnung angezeigt werden, wenn ListViews auf layout_height = wrap_content gesetzt wird, da dies so viele Leistungsprobleme verursacht.
Nathanielwolf
53

Versuchen Sie es mit match_parentder layout_heightEigenschaft der Listenansicht. Es wird verhindert getView(), so oft angerufen zu werden.

Fred T.
quelle
4
Beachten Sie, dass Fred T die layout_height der LIST VIEW angibt ... nicht die Zeile, was ich immer wieder versucht habe
mblackwell8
8
Beachten Sie, dass fill_parentdies match_parentfür API Level 8 und höher ersetzt werden sollte.
Jason Axelson
Ich denke, dass die Methode getView () mehrmals aufgerufen wird, indem Breite und Höhe als match_parent für ListView festgelegt werden, um nur das Leistungsproblem zu lösen.
Cuong Vo
45

Ich habe dieses Problem behoben, als ich sowohl layout_width als auch layout_height in match_parent geändert habe (es hat nicht geholfen, nur layout_height zu ändern ).


Hilfreiche Notiz Achten Sie darauf, ob Sie verschachtelte Elemente haben. Sie müssen die "höchste" in " match_parent " ändern . Hoffe es hilft jemandem.

Eugene Chumak
quelle
Ein Wort Kumpel "genial", aber ich weiß nicht, warum wrap_content ein Problem verursacht. Dies löste mein Problem.
Android Killer
@AndroidKiller , wenn Sie verwenden wrap_content, dann ListViewweiß nicht , wie vielen Listeneintrag gibt es Liste Inhalt werden, das ist , warum ListViewversucht so viele Zeilen zu erstellen , wie es ist denkt Anzeige geeignet, und wenn Sie fill_parentoder match_parent, ListViewdenkt, in Ordnung, meine Größe ist xund ich brauche die nAnzahl der Zeilen zu zeigen.
Adil Soomro
Ich danke dir sehr ! Mit diesem Verhalten änderten sich die Bilder aufgrund mehrerer Anrufe zufällig ... Dies ist von Anfang an in Ordnung.
Chostakovitch
7

Ich kann Ihre "Warum" -Frage nicht beantworten, aber ich habe definitiv eine Lösung für das Problem des irritierenden Problems " Wiederholung von ListView-Elementen " (wenn Sie Elemente in Ihrer Sammlung haben, die größer als die Bildschirmhöhe sind).

Wie viele der oben genannten Personen bereits erwähnt haben, behalten Sie die Eigenschaft android: layout_height derBehalten Sie, wie ListVew- Tags als fill_parent bei .

Bei der Funktion getView () besteht die Lösung darin, eine statische Klasse namens aufzurufen ViewHolder zu verwenden . Schauen Sie sich dieses Beispiel an. Es führt erfolgreich die Aufgabe aus, alle Elemente in ur Array oder ArrayCollection hinzuzufügen.

Hoffe das hilft Freunden !!

Beste Grüße, Siddhant

Siddhant
quelle
Seien Sie vorsichtig bei der Verwendung des ViewHolder-Musters in der frühen Android-Version (vor ICS), da dies zu einer MemoryLeak-Ausnahme führen kann. Fühlen Sie sich frei, es in ICS + -Versionen zu verwenden.
Vahid Ghadiri
@Siddhant danke Ich verbringe einen Tag damit, herauszufinden, warum Bilder in meinem Bild wiederholt GridViewund geändert werden, android:layout_heightund android:layout_widtharbeite nicht für mich. Aber mit ViewHolderfesten, sich wiederholenden Bildern für mich ging ich durch dieses Tutorial android-vogue.blogspot.com/2011/06/…
Nikolay Podolnyy
4

Fragen: Warum ruft der Adapter getView () oft auf? Antwort: Wenn Listview beim Scrollen gerendert wird, wird die Ansicht mit den nächsten Ansichten aktualisiert, für die der Adapter Ansichten aufrufen muss, indem er getView () aufruft.

Fragen: Warum werden Aufrufe geringer, wenn die Breite und Höhe der Listenansicht auf fill_parent gesetzt ist? Antwort: Da der Inflator die feste Größe für den Bildschirmbereich für die Liste hat, wird er einmal berechnet, um die Ansichten auf dem Bildschirm zu rendern.

Hoffe, es wird Ihre Anfrage lösen.

Jitain Sharma
quelle
Tolle Erklärung. Aber match_parent ist möglicherweise die bessere Wahl.
Fattie
Ja @JoeBlow, es wird empfohlen, match_parent anstelle von fill_parent ab API 2.4 / 3.0 zu verwenden
jitain sharma
4

Ich hatte das gleiche Problem mit der Dropdown-Liste in einer AutoCompleteTextView. Ich habe zwei Tage lang mit dem Problem gekämpft, bis ich hier ankomme und Sie mir die Lösung zeigen.

Wenn ich dropDownHeight = "match_parent" schreibe, ist das Problem behoben. Jetzt hängt das Problem mit der Benutzeroberfläche zusammen (wenn Sie ein Element haben, ist die Dropdown-Liste zu groß), aber das Problem mit mehreren Anrufen (viel wichtiger) ist behoben.

Danke dir!!

Pau Arlandis Martinez
quelle
2

"Warum wird getview für jede Zeile dreimal aufgerufen?" Weil getView aufgerufen wird, wenn Sie in der Listenansicht scrollen und besser gesagt, dass es aufgerufen wird, wenn die Position einer Ansicht Ihrer Liste geändert wird!

Ungureanu Liviu
quelle
ok, aber die Ansicht hat sich nicht geändert. Es gibt vier Zeilenansichten auf dem Bildschirm, wenn die Aktivität beginnt, und ohne Bildlauf oder Benutzereingaben bekomme ich drei getView-Aufrufe für jede Zeile ...
edzillion
Diese Antwort ist falsch. Die richtige Antwort ist, dass die Verwendung von ** wrap_content ** eine große Anzahl von Aufrufen von getView verursacht.
Fattie
2

Ich habe das gleiche Problem. Wenn ich die Höhe auf fill_parent eingestellt habe, erhalte ich "normalerweise" 2 Aufrufe pro Zeile. Wenn ich jedoch die Höhe meiner ListView auf den exakten Wert setze, z. B. 300 dp, erhalte ich genau einen GetView-Aufruf pro Zeile.

Es scheint mir also, dass die einzige Möglichkeit darin besteht, zuerst die Höhe des Bildschirms zu bestimmen und dann programmgesteuert die Höhe von listvilew auf diesen Wert zu setzen. Ich mag es nicht Ich hoffe es gibt einen besseren Weg.

Bobetko
quelle
1
Dank dafür. Es ist lustig - diese Frage schlummert seit Monaten, nur um in den letzten Wochen einige Kommentare zu erhalten. Ich mache dieses Projekt nicht mehr, aber da ich etwas Zeit habe, werde ich es versuchen und dann hierher zurückkehren, um es zu bestätigen. Danke noch einmal.
Edzillion
Gleich hier bekomme ich zwei Anrufe pro Zeile. Zeilen 0,1,2,3 und dann 100 ms später weitere 0,1,2,3 - was ein Schmerz im Arsch ist, wenn Statistiken darüber gesammelt werden, welche Zeilen gesehen werden
Jemand irgendwo
2

Für alle, die noch (nach dem Einstellen heightdes ListViewto match_parent) festsitzen (wie ich):

Sie müssen auch heightdas übergeordnete Layout auf einstellen match_parent.

Siehe Beispiel unten. Das LinearLayoutist der Elternteil hier:

<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>
B001 ᛦ
quelle
1

Dies kann zu spät kommen, aber wenn Sie verwenden, layout_weightdenken Sie daran, immer einzustellenlayout_width="0dp"

Kuti Gbolahan
quelle
0

Ich habe diese Lösung gemacht, vielleicht ist sie nicht die beste, aber sie macht die Arbeit ...

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

dann =

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

ich hoffe es hilft!

yago
quelle