2010-09-22 4 views
6

J'ai implémenté les images de chargement paresseux dans mon ListView. J'utilise un AsyncTask pour télécharger l'image à partir d'Internet et la lier à ImageView dans l'UIThread.Lazy-chargement d'images dans ListView sur Android

Cela fonctionne sauf que lorsque je fais défiler les ListView varient rapidement, les images téléchargées sont parfois liées aux mauvais éléments de la liste. Je suppose que le problème provient de la réutilisation de convertView dans le BaseAdapter Des idées pour le résoudre?

Merci beaucoup.

EDIT: je poste la réponse comme suit:

public void setBitmap(int position, Bitmap image) { 
    View itemView = mListView.getChildAt(position - mListView.getFirstVisiblePosition()); 
    if (itemView != null) { 
     ImageView itemImageView = (ImageView) itemView.findViewById(R.id.item_imageview); 
     itemImageView.setImageBitmap(image); 
    } 
} 
+0

J'ai essayé l'exemple ici http://android-developers.blogspot.com/2010/07/multithreading-for-performance.html Il fonctionne, mais encore eu la chance de lier la mauvaise image. L'astuce compare l'instance de la tâche semble la même que la réponse de Janusz. Mais il utilise une référence faible pour contenir l'instance de ImageView. Besoin de creuser plus profond. Des idées? – shiami

Répondre

2

Créer une fonction appelée void setBitmap(Bitmap bitmap, int position) ou similaire dans votre adaptateur. Laissez votre AsyncTask appeler cette méthode lorsqu'un nouveau bitmap est disponible. Cette méthode peut ensuite appeler notifyDataSetChanged() sur l'UI-Thread lui-même pour assurer l'actualisation des vues. Maintenir des références aux vues dans un adaptateur (même en les maintenant dans une asyncTask) est dangereux!

+0

Ce n'est pas le problème que shiami a. Il réutilise les vues qui ont un jeu d'images et parce que l'image correcte est définie dans une tâche asynchrone, l'ancienne image est affichée jusqu'à ce que la nouvelle image soit chargée, etc. – Janusz

+1

Oui, c'est le cas. J'ai déjà travaillé sur un projet où exactement ce comportement s'est produit, et l'utilisation de setTag() ou getTag() était juste une solution de contournement laide. La solution consistait à définir le bitmap directement dans la méthode getView(), elle-même déclenchée par notifyDataSetChanged(). Bien sûr, cela conduira à un rafraîchissement de toutes les vues qui sont visibles, ce qui pourrait provoquer un bégaiement notable. Pour actualiser une seule vue, cette solution pourrait être meilleure: http: // stackoverflow.com/questions/3724874/android-update-single-item-in-list – mreichelt

+0

Merci pour le conseil. Mais où puis-je obtenir l'instance de vue correcte pour lier mon image téléchargée en fonction de la position donnée? – shiami

7

Deux problèmes se posent lors du chargement paresseux d'images dans un ListView.

  1. Les anciennes images sont toujours affichées jusqu'à ce que les nouvelles soient chargées. C'est facile, il suffit de définir le ImageView à une image est en cours de chargement ou le rendre invisible avant de commencer le téléchargement de l'image.
  2. Le deuxième problème est plus difficile à résoudre. Imaginez que vous faites défiler très rapidement votre liste. Vos vues peuvent maintenant être recyclées avant que l'ancienne AsyncTask ait fini de charger l'image. Vous avez maintenant deux tâches en cours d'exécution que dans la méthode onPostExecute définira une image à l'imageview. Maintenant, pendant une courte période, la mauvaise image sera affichée jusqu'à ce que la deuxième tâche se termine, ou pire encore pour une raison liée au réseau, ils ne finissent pas dans l'ordre dans lequel ils ont démarré et la mauvaise image remplace l'image correcte. Pour résoudre ce problème, vous devez vérifier quelle image doit être affichée après la fin de la tâche. Dans la classe View sont deux méthodes pour des choses EXACT comme celui-ci:

    setTag et getTag Vous pouvez lier un objet à la imageview qui vient dans votre esprit. Dans la plupart des cas, j'utilise setTag pour lier l'URL de l'image en tant que String à l'imageview avant de démarrer une tâche. Maintenant, je peux lancer getTag en String une fois la tâche terminée et comparer l'URL qui doit être affichée avec l'URL que j'ai téléchargée et ne définir l'image que si nécessaire.

+0

Merci! Le premier problème que vous avez décrit ne me semble pas une solution. Parce que le 'ImageView' sera lié plusieurs fois lorsque le défilement varie rapidement. Ainsi, le réglage de ImageView une image de chargement ou invisible ne fonctionne pas. – shiami

+0

La méthode getTag semble ne pas lier immédiatement 'ImageView' car la balise est modifiée après le défilement. Il est seulement mis à jour après défilement lent et retour. Y a-t-il un pas que je fais incorrectement? – shiami

+0

Je ne comprends pas ce que vous voulez dire avec le getTag ne liera pas l'ImageView immédiatement, donc je ne peux pas dire si vous faites quelque chose de mal. – Janusz