niestandardowa metoda listview adaptera getView jest wywoływana wiele razy i bez spójnej kolejności

162

Mam niestandardowy adapter listy:

class ResultsListAdapter extends ArrayAdapter<RecordItem> {

w nadpisanej metodzie 'getView' robię wydruk, aby sprawdzić, jaka jest pozycja i czy jest to convertView, czy nie:

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

Wynik tego (kiedy lista jest wyświetlana po raz pierwszy, na razie brak danych wejściowych użytkownika)

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, chociaż nie mogłem znaleźć tego wyraźnie, getView () jest wywoływana tylko dla widocznych wierszy. Ponieważ moja aplikacja zaczyna się od czterech widocznych wierszy, przynajmniej numery pozycji zmieniające się od 0 do 3 mają sens. Ale reszta to bałagan:

  • Dlaczego getview jest wywoływane dla każdego wiersza trzy razy?
  • Skąd pochodzą te ConvertViews, kiedy jeszcze nie przewinąłem?

Zrobiłem trochę badań i bez uzyskania dobrej odpowiedzi zauważyłem, że ludzie kojarzą ten problem z problemami z układem. Więc na wszelki wypadek, oto układ, który zawiera listę:

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

oraz układ każdego pojedynczego rzędu:

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

Dziękuję za Twój czas

edzillion
źródło

Odpowiedzi:

271

To nie jest problem, nie ma absolutnie żadnej gwarancji, w jakiej kolejności getView()zostaniesz wezwany ani ile razy. W twoim konkretnym przypadku robisz najgorszą możliwą rzecz z a ListView, nadając mu height=wrap_content. Wymusza ListViewto odmierzenie kilku elementów potomnych z adaptera w czasie projektowania, aby wiedzieć, jak duży powinien być. To właśnie zapewnia ListViewto, convertViewsco widzisz, przekazane getView()jeszcze przed przewinięciem.

Romain Guy
źródło
10
Nie uważam tego za dobry powód. W porządku, jeśli chcesz mieć możliwość zmiany, ale obecnie nie odpowiada, dlaczego robi to zachowanie?
cdpnet
45
@cdpnet On nie powiedział, dlaczego to robi, twój ListViewma wysokość wrap_content, więc to nie jest pewny, że to wysokość dlatego ustanawia niektóre dzieci się jako rodzaj testera, aby zobaczyć, co będzie pasować. zmienić ListView, aby fill_parentnastępnie sprawdzić logi liczba wezwań powinna zostać radykalnie ograniczona.
Blundell,
4
Zachowanie między 2.3 a 4.x era ListView uległo znacznej zmianie. Na wynos jest to, że ponieważ ListView został zaprojektowany, twoje komórki / list_items muszą być czystymi widokami i muszą być zoptymalizowane do szybkiego rysowania. Zgodnie z dyskusją Google I / O w ListView, w której przedstawił Romain, można wywołać getView tylko po to, aby zoptymalizować renderowanie widoku i odrzucić wynik. Chociaż moim zdaniem nie jest to tak przyjazne dla programistów jak iOS UITableView, tak właśnie jest.
Cameron Lowell Palmer
4
Dziękuję bardzo! Nie rozumiem, dlaczego metoda getView () jest tak często wywoływana. I swithced wrap_content do fill_parent i teraz moja aplikacja jest szybka ponownie :)
Julia Hexen
28
Podczas ustawiania ListViews na layout_height = wrap_content powinno pojawić się ostrzeżenie, ponieważ powoduje to wiele problemów z wydajnością.
nathanielwolf
53

Spróbuj z match_parentna layout_heightwłaściwości widoku listy. Zapobiegnie getView()tak częstemu wywoływaniu.

Fred T.
źródło
4
Zauważ, że Fred T określa layout_height WIDOKU LISTY ... a nie wiersz, co próbowałem
wielokrotnie
8
Należy pamiętać, że fill_parentnależy je zastąpić match_parentdla poziomu API 8 i wyższych.
Jason Axelson
Myślę, że metoda getView () wywoła wiele razy, ustawiając szerokość i wysokość jako match_parent dla ListView tylko rozwiązuje problem z wydajnością.
Cuong Vo
45

Pozbyłem się tego problemu, kiedy zmieniłem zarówno layout_width, jak i layout_height na match_parent (zmiana tylko layout_height nie pomogła).


Przydatna uwaga, uważaj, jeśli masz zagnieżdżone elementy. Musisz zmienić „najwyższy” na match_parent . Mam nadzieję, że to komuś pomoże.

Eugene Chumak
źródło
jedno słowo kolego "super", ale nie wiem, dlaczego wrap_content stwarza problem. To rozwiązało mój problem.
Android Killer,
@AndroidKiller, kiedy używasz wrap_content, ListViewnie wiesz, ile elementów listy jest dostępnych, aby stać się treścią listy, dlatego ListViewpróbuje utworzyć tyle wierszy, ile uważa za odpowiednich do wyświetlenia, a kiedy używasz fill_parentlub match_parent,ListView myśli, w porządku, mój wzrost jest xi potrzebuję nliczby wierszy do wyświetlenia.
Adil Soomro,
Dziękuję bardzo ! Przy takim zachowaniu obrazy zmieniały się losowo z powodu wielu wywołań ... Teraz to jest w porządku od samego początku.
Chostakovitch
7

Nie jestem w stanie odpowiedzieć na Twoje pytanie "Dlaczego" ale na pewno mam rozwiązanie problemu irytującego " powtarzania się elementów ListView problemu „ ” (jeśli masz w swojej kolekcji elementy, które są większe niż wysokość ekranu).

Jak wielu ludzi wyżej wspomniałem, utrzymać android: layout_height własność ListVew znacznika jako fill_parent .

Jeśli chodzi o funkcję getView (), rozwiązaniem jest użycie statycznej klasy o nazwie ViewHolder . Sprawdź to przykład. Z powodzeniem wykonuje zadanie dodawania wszystkich elementów w ur Array lub ArrayCollection.

Mam nadzieję, że to pomoże przyjaciołom!

Z poważaniem, Siddhant

Siddhant
źródło
Zachowaj ostrożność przy używaniu wzorca ViewHolder we wczesnej wersji Androida (przed ICS), ponieważ może to prowadzić do wyjątku MemoryLeak. możesz go używać w wersjach ICS +.
Vahid Ghadiri,
@Siddhant dzięki, spędziłem dzień, aby dowiedzieć się, dlaczego powtarzam obrazy w moich GridViewi zmieniających się android:layout_heighti android:layout_widthnie działa dla mnie. Ale używając ViewHolderstałych, powtarzających się obrazów, przeszedłem przez ten samouczek android-vogue.blogspot.com/2011/06/...
Nikolay Podolnyy
4

Pytania: Dlaczego adapter wielokrotnie wywołuje metodę getView ()? Odp: Gdy Listview renderuje się podczas przewijania, odświeża swój widok kolejnymi nadchodzącymi widokami, dla których adapter musi uzyskać widoki, wywołując metodę getView ().

Zapytania: Dlaczego wywołuje mniejszą wartość, jeśli szerokość i wysokość widoku listy są ustawione na fill_parent? Odp: Ponieważ inflator ma stały rozmiar obszaru ekranu dla listy, oblicza go raz w celu renderowania widoków na ekranie.

Mam nadzieję, że to rozwiąże Twoje pytanie.

jitain sharma
źródło
Świetne wyjaśnienie. Ale match_parent może być lepszym wyborem.
Fattie
Tak @JoeBlow, zaleca się używanie match_parent zamiast fill_parent od API 2.4 / 3.0 i
nowszych
4

Miałem ten sam problem z menu rozwijanym w AutoCompleteTextView. Walczyłem z tym problemem przez dwa dni, aż tu dotarłem, a ty pokazałeś mi rozwiązanie.

Jeśli napiszę dropDownHeight = "match_parent", problem został rozwiązany. Teraz problem jest związany z interfejsem użytkownika (gdy masz jeden element, lista rozwijana jest zbyt duża), ale problem wielu wywołań (o wiele ważniejszy) został rozwiązany.

Dziękuję Ci!!

Pau Arlandis Martinez
źródło
2

„Dlaczego getview jest wywoływane dla każdego wiersza trzy razy?” Ponieważ getView jest wywoływane, gdy przewijasz listę w widoku listy, a mówiąc lepiej, jest wywoływane, gdy pozycja widoku listy zostanie zmieniona!

Ungureanu Liviu
źródło
ok, ale widok się nie zmienił. po rozpoczęciu działania na ekranie są cztery widoki wierszy i bez przewijania lub w ogóle wprowadzania danych przez użytkownika otrzymuję trzy wywołania getView dla każdego wiersza ...
edzillion
Ta odpowiedź jest błędna. Prawidłowa odpowiedź jest taka, że ​​użycie ** wrap_content ** powoduje ogromną liczbę wywołań getView.
Fattie
2

Mam ten sam problem. Jeśli wysokość jest ustawiona na fill_parent, otrzymuję „zwykle” 2 wywołania w każdym wierszu. Ale jeśli ustawię wysokość mojego ListView na dokładną wartość, powiedzmy na 300dp, otrzymam dokładnie jedno wywołanie GetView na wiersz.

Wydaje mi się więc, że jedynym sposobem jest najpierw określenie wysokości ekranu, a następnie programowe ustawienie wysokości listvilew na tę wartość. Nie podoba mi się to. Mam nadzieję, że jest lepszy sposób.

bobetko
źródło
1
Dziękuję za to. To zabawne - to pytanie pozostaje uśpione od miesięcy, by w ciągu ostatnich kilku tygodni pojawiło się kilka komentarzy. Nie zajmuję się już tym projektem, ale ponieważ mam trochę czasu, spróbuję i wrócę tutaj, aby potwierdzić. dzięki jeszcze raz.
edzillion
to samo tutaj, otrzymuję dwa połączenia na wiersz. Wiersze 0, 1, 2, 3, a następnie 100 ms później kolejne 0, 1, 2, 3 - co jest upierdliwe przy zbieraniu statystyk dotyczących widocznych rzędów
Someone Somewhere
2

Dla wszystkich z was, którzy nadal (po ustawieniu heightopcji ListViewdo match_parent) utknęli (tak jak ja):

Musisz także ustawić heightukład nadrzędny namatch_parent .

Zobacz przykład poniżej. Tutaj LinearLayoutjest rodzicem:

<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 ᛦ
źródło
1

Może się to spóźnić, ale jeśli używasz, layout_weightpamiętaj, aby zawsze ustawićlayout_width="0dp"

Kuti Gbolahan
źródło
0

zrobiłem to rozwiązanie, może nie jest najlepsze, ale działa ...

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

następnie =

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

mam nadzieję, że to pomoże!

yago
źródło