2009-09-11 20 views
6

Este es un escenario muy común: mostrar imágenes en un ListView que deben descargarse de Internet.Android - Problema con la carga lenta de imágenes en un ListView

Ahora tengo una subclase personalizada de ArrayAdapter que utilizo para ListView. En mi implementación getView() del ArrayAdapter, genero un hilo separado para cargar una imagen. Una vez realizada la carga, busca el ImageView adecuado y establece la imagen con ImageView.setImageDrawable(). Así que la solución que utilicé es similar a esta: Lazy load of images in ListView

El problema que tengo es que tan pronto como realizo la llamada a setImageDrawable() en el ImageView, el ListView refresca de alguna manera todas las filas actualmente visibles en ¡la lista! Esto resulta en una especie de bucle infinito:

  1. getView() se llama
  2. se genera
  3. hilo para cargar la imagen
  4. se carga imagen
  5. ; setImageDrawable() se llama el ImageView
  6. ListView lo recoge por alguna razón y se refresca
  7. Para el ListView para refrescar, getView() se llama para cada fila visible, por lo que volver al paso 1 y toda la cosa se repite a sí mismo

Por lo que puedo ver, la solución propuesta en "Android - ¿Cómo puedo hacer una carga de imágenes en ListView?" (ver enlace arriba) simplemente no funciona. Puede parecer que sí, pero funcionará muy lento porque, en el fondo, sigue recargándose las filas visibles actualmente.

¿Alguien se encontró con esto antes y/o tiene una solución para esto?

Respuesta

2

En la solución vinculada, fetchDrawableOnThread() solo debe invocarse si la vista no tiene el drawable correcto.

Una vista no tiene un drawable si getDrawable() devuelve nulo.

Si está reutilizando ranuras, considera que debe ir más allá y administrar el estado. Si sus vistas tienen una variable miembro que almacena la URL y un booleano para indicar si está cargada, sería fácil saber si llamar al fetchDrawableOnThread() o no, por ejemplo.

Me gustaría especular que el sorteo toString() detalla la ruta desde la que se cargó la imagen. (Si no es así, puede subclasificar el drawable devuelto para que así sea). En este caso, puede evitar el booleano descrito anteriormente y simplemente hacer una comparación para determinar si es el derecho dibujable o si buscar un reemplazo.

Además, su getView() en una fila visible debe garantizar que aquellos que ya no se vean se descarguen, para evitar el agotamiento de la memoria. Una delicadeza sería mover las imágenes que ya no son visibles a las referencias suaves (para que se descarguen cuando se necesite memoria) como se señaló en otro cartel del hilo original.

+1

Sí, estoy usando un mapa para almacenar en caché las imágenes. Pero eso no importa, ya que finalmente sigo llamando a setImageDrawable(), que de nuevo desencadena la actualización. Si pudiera de alguna manera desactivar el refesh, resolvería mi problema. No uso SoftReferences aún (pero lo haré), pero eso es solo una optimización de memoria, esto no resuelve el ciclo infinito –

+0

buen punto No entendí lo que quería decir con "thread is spawned to * load * image" . Voy a reescribir mi respuesta – Will

+0

Gracias por sus respuestas rápidas :) Lo que está describiendo resuelve el problema cuando no está reutilizando las vistas para mostrar filas como yo. Al reutilizar las filas me refiero al uso del argumento "convertView" que se da a getView(). ¿Conoces una solución para esto al reutilizar las vistas de fila? (porque en ese caso TIENE que llamar a setImageDrawable() cada vez) –

3

He utilizado el código en siguiente enlace: another stackoverflow question

hice pequeños cambios con el fin de resolver el reciclaje vista problem.I establecer la dirección URL de la imagen a Tag de imageview en el adaptador.El siguiente código contiene mi solución que resuelve el problema de reciclaje:

public void fetchDrawableOnThread(final String urlString, final ImageView imageView,Drawable drw) { 

    imageView.setImageDrawable(drw);//drw is default image 
    if (drawableMap.containsKey(urlString)) { 
     if(imageView.getTag().toString().equals(urlString)) 
     { 
      imageView.setImageBitmap(drawableMap.get(urlString)); 
      imageView.invalidate(); 
      return; 
     } 

    } 

    final Handler handler = new Handler() { 
     @Override 
     public void handleMessage(Message message) { 
      BitmapWrapper wrapper = (BitmapWrapper)message.obj; 
      if(wrapper.imageurl.equals(imageView.getTag().toString())) 
      { 
       imageView.setImageBitmap((Bitmap)wrapper.bitmap); 
       imageView.invalidate(); 
      } 

     } 
    }; 

    Thread thread = new Thread() { 
     @Override 
     public void run() { 
      //TODO : set imageView to a "pending" image 

      Bitmap drawable = fetchDrawable(urlString); 
      BitmapWrapper wrapper = new BitmapWrapper(); 
      wrapper.bitmap = drawable; 
      wrapper.imageurl = urlString; 
      Message message = handler.obtainMessage(1, wrapper); 
      handler.sendMessage(message); 
     } 
    }; 
    thread.start(); 
} 


    public class BitmapWrapper 
{ 
    public Bitmap bitmap; 
    public String imageurl; 
} 
3

Tuve el mismo problema.

Después de casi 2 días de pesada depuración/optimización y tratando de averiguar, por qué mi getView() se llama para todos los puntos de vista una y otra vez cuando se utiliza setImageBitmap() en una fila, se me ocurrió una solución sucia:

1) Extender una costumbre ImageView que se utiliza para todas las imágenes en la lista de

2) en este ImageView sobrescribir el método

@Override 
public void requestLayout() 
{ 
    return; 
} 

3) Sucio, pero para mí funciona

4) Profit;)

+1

Cuando las imágenes en la lista son del mismo tamaño y su vista de imagen también es de tamaño fijo (no se ajusta el contenido) puede usarse. Esto me ayudó, gracias :) – Mark

Cuestiones relacionadas