La méthode getView de l'adaptateur de liste personnalisée est appelée plusieurs fois et sans ordre cohérent

162

J'ai un adaptateur de liste personnalisé:

class ResultsListAdapter extends ArrayAdapter<RecordItem> {

dans la méthode 'getView' remplacée, je fais une impression pour vérifier quelle est la position et s'il s'agit d'un convertView ou non:

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

La sortie de ceci (lorsque la liste est affichée pour la première fois, aucune entrée utilisateur pour le moment)

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, bien que je ne puisse pas le trouver indiqué explicitement, getView () n'est appelé que pour les lignes visibles. Étant donné que mon application commence par quatre lignes visibles, au moins les numéros de position passant de 0 à 3 ont du sens. Mais le reste est un gâchis:

  • Pourquoi getview est-il appelé trois fois pour chaque ligne?
  • D'où viennent ces convertViews lorsque je n'ai pas encore fait défiler?

J'ai fait un peu de recherche, et sans obtenir de bonne réponse, j'ai remarqué que les gens associaient ce problème à des problèmes de mise en page. Donc, au cas où, voici la mise en page qui contient la liste:

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

et la disposition de chaque ligne individuelle:

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

Merci pour votre temps

edzillion
la source

Réponses:

271

Ce n'est pas un problème, il n'y a absolument aucune garantie sur l'ordre dans lequel getView()sera appelé ni combien de fois. Dans votre cas particulier, vous faites la pire chose possible avec un ListViewen lui donnant un height=wrap_content. Cela oblige ListViewà mesurer quelques enfants hors de l'adaptateur au moment de la mise en page, pour savoir quelle doit être sa taille. C'est ce que fournit ListViewle que convertViewsvous voyez passé getView()avant même de faire défiler.

Romain Guy
la source
10
Je ne trouve pas cela une bonne raison. C'est bien si vous voulez pouvoir changer, mais actuellement, cela ne répond pas pourquoi il fait ce comportement?
cdpnet
45
@cdpnet Il a dit pourquoi il le faisait, vous avez ListViewune hauteur de wrap_content, donc il n'est pas sûr de sa hauteur, donc il présente des enfants comme une sorte de testeur pour voir ce qui convient. Changez votre ListViewpour fill_parentpuis vérifiez vos journaux les appels devraient être considérablement réduits.
Blundell
4
Le comportement entre les ères 2.3 et 4.x de ListView a considérablement changé. Ce qu'il faut retenir, c'est que, comme ListView est conçu, vos cells / list_items doivent être des vues pures et ils doivent être optimisés pour dessiner rapidement. Selon la conférence Google I / O sur ListView, où Romain a présenté, getView pourrait être appelé uniquement pour optimiser le rendu de la vue et ignorer le résultat. Bien qu'à mon avis, ce ne soit pas aussi convivial pour les développeurs que iOS UITableView, c'est comme ça.
Cameron Lowell Palmer
4
Merci beaucoup! Je n'ai pas compris pourquoi getView () est appelé si souvent. J'ai basculé wrap_content vers fill_parent et maintenant mon application est à nouveau rapide :)
Julia Hexen
28
Il devrait y avoir un avertissement lors de la définition de ListViews sur layout_height = wrap_content car cela cause de nombreux problèmes de performances.
nathanielwolf
54

Essayez avec match_parentla layout_heightpropriété de la vue de liste. Cela évitera getView()d'être appelé si souvent.

Fred T
la source
4
Notez que Fred T spécifie le layout_height de la LIST VIEW ... pas la ligne, ce que j'ai essayé encore et encore
mblackwell8
8
Notez que cela fill_parentdoit être remplacé par match_parentpour le niveau d'API 8 et supérieur.
Jason Axelson
Je pense que la méthode getView () appellera plusieurs fois, en définissant la largeur et la hauteur comme match_parent pour le ListView ne résout que le problème de performances.
Cuong Vo
45

Je me suis débarrassé de ce problème lorsque j'ai changé à la fois layout_width et layout_height en match_parent (changer uniquement layout_height n'a pas aidé).


Remarque utile: faites attention si vous avez des éléments imbriqués. Vous devez remplacer celui "le plus élevé" par match_parent . J'espère que ça aide quelqu'un.

Eugène Chumak
la source
un mot copain "génial" mais je ne sais pas pourquoi wrap_content crée un problème. Cela a résolu mon problème.
Android Killer
@AndroidKiller lorsque vous utilisez wrap_content, alors ListViewvous ne savez pas combien d'élément de liste il y a pour devenir le contenu de List, c'est pourquoi ListViewessaie de créer autant de lignes qu'il pense qu'il convient d'afficher, et lorsque vous utilisez fill_parentou match_parent, ListViewpense, d'accord, ma hauteur est xet j'ai besoin du nnombre de lignes à afficher.
Adil Soomro
Merci beaucoup ! Avec ce comportement, les images changeaient aléatoirement à cause de plusieurs appels ... Maintenant c'est ok depuis le début.
Chostakovitch
7

Je ne suis pas en mesure de répondre à votre question "Pourquoi", mais j'ai définitivement une solution au problème de l'irritant " Répétition des éléments de ListView " (si vous avez des éléments dans votre collection qui dépassent la hauteur de l'écran).

Comme beaucoup de gens ont mentionné ci - dessus, garder l' androïde: layout_height propriété du ListVew tag comme fill_parent .

Et à propos de la fonction getView (), la solution consiste à utiliser une classe statique appelée ViewHolder . Regardez cet exemple. Il effectue avec succès la tâche d'ajouter tous les éléments dans ur Array ou ArrayCollection.

J'espère que cela aide les amis !!

Meilleures salutations, Siddhant

Siddhant
la source
Soyez prudent lorsque vous utilisez le modèle ViewHolder dans la première version d'Android (avant ICS), car cela peut vous conduire à une exception MemoryLeak. n'hésitez pas à l'utiliser dans les versions ICS +.
Vahid Ghadiri
@Siddhant merci, je passe une journée à trouver la raison de répéter les images dans mon GridViewet de changer android:layout_heightet android:layout_widthne fonctionne pas pour moi. Mais en utilisant ViewHolderdes images répétées fixes pour moi, j'ai parcouru ce tutoriel android-vogue.blogspot.com/2011/06
Nikolay Podolnyy
4

Ques: Pourquoi Adapter appelle-t-il getView () plusieurs fois? Réponse: Comme Listview est rendu lors du défilement, sa vue est actualisée avec les prochaines vues à venir, pour lesquelles l'adaptateur doit obtenir des vues en appelant getView ().

Ques: Pourquoi appelle-t-il moins si la largeur et la hauteur de la vue de liste sont définies sur fill_parent? Réponse: Parce que le gonfleur a la taille fixe de la zone d'écran pour la liste, il calcule une fois pour rendre les vues à l'écran.

J'espère que cela résoudra votre requête.

jitain sharma
la source
Excellente explication. Mais match_parent peut être un meilleur choix.
Fattie
Oui @JoeBlow, il est recommandé d'utiliser match_parent au lieu de fill_parent à partir de l'API 2.4 / 3.0
jitain sharma
4

J'avais le même problème avec la liste déroulante dans un AutoCompleteTextView. Je me suis battu avec le problème pendant deux jours jusqu'à ce que j'arrive ici et que vous me montriez la solution.

Si j'écris dropDownHeight = "match_parent", le problème est résolu. Maintenant, le problème est lié à l'interface utilisateur (lorsque vous avez un élément, la liste déroulante est trop grande) mais le problème des appels multiples (beaucoup plus important) est résolu.

Je vous remercie!!

Pau Arlandis Martinez
la source
2

"Pourquoi getview est-il appelé trois fois pour chaque ligne?" Parce que getView est appelé lorsque vous faites défiler sur listview et pour dire mieux alors qu'il est appelé lorsque la position d'une vue de votre liste est modifiée!

Ungureanu Liviu
la source
ok, mais la vue n'a pas changé. il y a quatre vues de ligne à l'écran lorsque l'activité commence, et sans défilement ni aucune entrée de l'utilisateur, je reçois trois appels getView pour chaque ligne ...
edzillion
Cette réponse est fausse. La bonne réponse est que l'utilisation de ** wrap_content ** entraîne un grand nombre d'appels à getView.
Fattie
2

J'ai le même problème. Si la hauteur est définie sur fill_parent, alors je reçois "généralement" 2 appels par ligne. Mais, si je règle la hauteur de ma ListView à la valeur exacte, disons à 300dp, alors je reçois exactement un appel GetView par ligne.

Donc, il me semble que le seul moyen est de déterminer d'abord la hauteur de l'écran, puis de définir par programme la hauteur de listvilew à cette valeur. Je n'aime pas ça. J'espère qu'il existe un meilleur moyen.

Bobetko
la source
1
Merci pour ça. C'est drôle - cette question est restée en sommeil depuis des mois maintenant pour que quelques commentaires apparaissent au cours des dernières semaines. Je ne fais plus ce projet mais comme j'ai un peu de temps, je vais essayer et revenir ici pour confirmer. Merci encore.
edzillion
même ici, je reçois deux appels par ligne. Rangées 0,1,2,3 puis 100ms plus tard encore 0,1,2,3 - ce qui est une douleur dans le cul lors de la collecte de statistiques sur les lignes vues
Someone Somewhere
2

Pour tous ceux qui (après avoir réglé heightle ListViewsur match_parent) sont toujours bloqués (comme moi):

Vous devez également définir le heightde la mise en page parent sur match_parent.

Voir l'exemple ci-dessous. Le LinearLayoutest le parent ici:

<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 ᛦ
la source
1

Cela arrive peut-être tard, mais si vous utilisez, layout_weightn'oubliez pas de toujours définirlayout_width="0dp"

Kuti Gbolahan
la source
0

J'ai fait cette solution, ce n'est peut-être pas la meilleure, mais fait le travail ...

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

alors =

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

J'espère que ça aide!

yago
la source