2010-05-26 17 views
39

Duplicar posible:
Android - How do I do a lazy load of images in ListViewLazy Load images on Listview in android (Beginner Level)?

estoy trabajando en la vista de lista con el adaptador personalizado. Quiero cargar las imágenes y la vista de texto en él. Las imágenes se cargan desde las URL de internet. Solo quiero mostrar las imágenes que son elementos visibles de la lista para el usuario. Me referí al Shelves opensource project example from romainguy, pero es complicado comprender el código. Para un nivel principiante, solo quiero saber cómo manejar la etiqueta entre el adaptador y la actividad. Desde el commonswareexample puedo configurar la etiqueta en el adaptador, pero no puedo mostrar la imagen correspondiente en el estado inactivo de la vista de lista. Por favor, ayúdame con tus ideas. Los códigos de muestra son más comprensibles.

Gracias.

EDIT:

La combinación de Efficient y Slow adaptador en ApiDemos es más útil para entender.

cambios realizados en el ejemplo de adaptador eficiente como esto:

public class List14 extends ListActivity implements ListView.OnScrollListener { 
// private TextView mStatus; 

private static boolean mBusy = false; 
static ViewHolder holder; 

public static class EfficientAdapter extends BaseAdapter { 
    private LayoutInflater mInflater; 
    private Bitmap mIcon1; 
    private Bitmap mIcon2; 

    public EfficientAdapter(Context context) { 
     // Cache the LayoutInflate to avoid asking for a new one each time. 
     mInflater = LayoutInflater.from(context); 

     // Icons bound to the rows. 
     mIcon1 = BitmapFactory.decodeResource(context.getResources(), 
       R.drawable.icon48x48_1); 
     mIcon2 = BitmapFactory.decodeResource(context.getResources(), 
       R.drawable.icon48x48_2); 
    } 

    /** 
    * The number of items in the list is determined by the number of 
    * speeches in our array. 
    * 
    * @see android.widget.ListAdapter#getCount() 
    */ 
    public int getCount() { 
     return DATA.length; 
    } 

    /** 
    * Since the data comes from an array, just returning the index is 
    * sufficent to get at the data. If we were using a more complex data 
    * structure, we would return whatever object represents one row in the 
    * list. 
    * 
    * @see android.widget.ListAdapter#getItem(int) 
    */ 
    public Object getItem(int position) { 
     return position; 
    } 

    /** 
    * Use the array index as a unique id. 
    * 
    * @see android.widget.ListAdapter#getItemId(int) 
    */ 
    public long getItemId(int position) { 
     return position; 
    } 

    /** 
    * Make a view to hold each row. 
    * 
    * @see android.widget.ListAdapter#getView(int, android.view.View, 
    *  android.view.ViewGroup) 
    */ 
    public View getView(int position, View convertView, ViewGroup parent) { 
     // A ViewHolder keeps references to children views to avoid 
     // unneccessary calls 
     // to findViewById() on each row. 

     // When convertView is not null, we can reuse it directly, there is 
     // no need 
     // to reinflate it. We only inflate a new View when the convertView 
     // supplied 
     // by ListView is null. 
     if (convertView == null) { 
      convertView = mInflater.inflate(R.layout.list_item_icon_text, 
        null); 

      // Creates a ViewHolder and store references to the two children 
      // views 
      // we want to bind data to. 
      holder = new ViewHolder(); 
      holder.text = (TextView) convertView.findViewById(R.id.text); 
      holder.icon = (ImageView) convertView.findViewById(R.id.icon); 

      convertView.setTag(holder); 
     } else { 
      // Get the ViewHolder back to get fast access to the TextView 
      // and the ImageView. 
      holder = (ViewHolder) convertView.getTag(); 
     } 
     if (!mBusy) { 
      holder.icon.setImageBitmap(mIcon1); 

      // Null tag means the view has the correct data 
      holder.icon.setTag(null); 
     } else { 
      holder.icon.setImageBitmap(mIcon2); 

      // Non-null tag means the view still needs to load it's data 
      holder.icon.setTag(this); 
     } 
     holder.text.setText(DATA[position]); 

     // Bind the data efficiently with the holder. 
     // holder.text.setText(DATA[position]); 

     return convertView; 
    } 

    static class ViewHolder { 
     TextView text; 
     ImageView icon; 
    } 
} 

private Bitmap mIcon1; 
private Bitmap mIcon2; 
@Override 
public void onCreate(Bundle savedInstanceState) { 
    mIcon1 = BitmapFactory.decodeResource(this.getResources(), 
      R.drawable.icon48x48_1); 
    mIcon2 = BitmapFactory.decodeResource(this.getResources(), 
      R.drawable.icon48x48_2); 
    super.onCreate(savedInstanceState); 
    setListAdapter(new EfficientAdapter(this)); 
    getListView().setOnScrollListener(this); 
} 

public void onScroll(AbsListView view, int firstVisibleItem, 
     int visibleItemCount, int totalItemCount) { 
} 

public void onScrollStateChanged(AbsListView view, int scrollState) { 
    switch (scrollState) { 
    case OnScrollListener.SCROLL_STATE_IDLE: 
     mBusy = false; 

     int first = view.getFirstVisiblePosition(); 
     int count = view.getChildCount(); 
     for (int i = 0; i < count; i++) { 

      holder.icon = (ImageView) view.getChildAt(i).findViewById(
        R.id.icon); 
      if (holder.icon.getTag() != null) { 
       holder.icon.setImageBitmap(mIcon1); 
       holder.icon.setTag(null); 
      } 
     } 

     // mStatus.setText("Idle"); 
     break; 
    case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL: 
     mBusy = true; 
     // mStatus.setText("Touch scroll"); 
     break; 
    case OnScrollListener.SCROLL_STATE_FLING: 
     mBusy = true; 
     // mStatus.setText("Fling"); 
     break; 
    } 
} 
private static final String[] DATA = { "Abbaye de Belloc", 
     "Abbaye du Mont des Cats", "Abertam", "Abondance", "Ackawi", 
     "Acorn", "Adelost", "Affidelice au Chablis", "Afuega'l Pitu"}; 
} 

funciona bien ahora. Pero cuando el estado de desplazamiento no está recargando la imagen correctamente. algunos intervalos de elementos no muestran las imágenes2. ese es el orden correcto de las imágenes que se están cargando. Pero no en todos los elementos de la lista. Se producen desajustes entre los intervalos de elementos sólidos. cómo corregirlo?

Respuesta

5

Praveen -

Como ya encontró su entrada de blog en esto, yo sólo quería empujarlo de nuevo a Stackoverflow para que otros puedan utilizarlo.

Aquí está la discusión básica: http://ballardhack.wordpress.com/2010/04/05/loading-remote-images-in-a-listview-on-android/

Y hay una clase Documenté después de que utiliza un hilo y una devolución de llamada para cargar las imágenes:

http://ballardhack.wordpress.com/2010/04/10/loading-images-over-http-on-a-separate-thread-on-android/

Actualización: hacer frente a sus excepción específica, creo que la vista devuelta en la lista de getChildAt no es ImageView - es cualquier vista de diseño que esté usando para contener la imagen y el texto.

actualización para incluir código relevante: (Por @ George-Stocker recomendación)

Aquí es el adaptador que estaba usando:

public class MediaItemAdapter extends ArrayAdapter<MediaItem> { 
    private final static String TAG = "MediaItemAdapter"; 
    private int resourceId = 0; 
    private LayoutInflater inflater; 
    private Context context; 

    private ImageThreadLoader imageLoader = new ImageThreadLoader(); 

    public MediaItemAdapter(Context context, int resourceId, List<MediaItem> mediaItems) { 
    super(context, 0, mediaItems); 
    this.resourceId = resourceId; 
    inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
    this.context = context; 
    } 

    @Override 
    public View getView(int position, View convertView, ViewGroup parent) { 

    View view; 
    TextView textTitle; 
    TextView textTimer; 
    final ImageView image; 

    view = inflater.inflate(resourceId, parent, false); 

    try { 
     textTitle = (TextView)view.findViewById(R.id.text); 
     image = (ImageView)view.findViewById(R.id.icon); 
    } catch(ClassCastException e) { 
     Log.e(TAG, "Your layout must provide an image and a text view with ID's icon and text.", e); 
     throw e; 
    } 

    MediaItem item = getItem(position); 
    Bitmap cachedImage = null; 
    try { 
     cachedImage = imageLoader.loadImage(item.thumbnail, new ImageLoadedListener() { 
     public void imageLoaded(Bitmap imageBitmap) { 
     image.setImageBitmap(imageBitmap); 
     notifyDataSetChanged();    } 
     }); 
    } catch (MalformedURLException e) { 
     Log.e(TAG, "Bad remote image URL: " + item.thumbnail, e); 
    } 

    textTitle.setText(item.name); 

    if(cachedImage != null) { 
     image.setImageBitmap(cachedImage); 
    } 

    return view; 
    } 
} 
+0

¿Sabes cómo obtener ese objeto de vista de imagen? Vi y usé tu código. Pero eso no carga las imágenes correctamente. Una o dos imágenes no completadas para perder. Y obtiene la excepción de Outofmemory también. ¿por qué? ¿Podrias tu? – Praveen

+0

Deberá rastrear a través de su jerarquía de vistas. Llamar a getChildAt (índice) en un ListView devuelve el elemento de vista de lista (una vista de Diseño) para el índice desde la parte superior de la lista visible. En ese objeto de vista, puede llamar a findViewById para obtener la vista de la imagen. – jwadsack

+0

Por favor, publique aquí las secciones relevantes de su blog. Sin eso, cuando esos enlaces mueren, la utilidad de tu respuesta muere con él. –

0

Por lo que puedo ver, la estática ViewHolder no está ayudando nada. Intente poner toda la función onScrollStateChanged entre /* y */, eliminando la línea static ViewHolder y cambiando holder = new ViewHolder(); a ViewHolder holder = new ViewHolder();.

+0

"Intenta poner la función enteraScrollStateChanged entre/* y * /". no tiene sentido. entonces, ¿cómo podemos rastrear el estado del desplazamiento para cargar las filas particulares en visible? – Praveen

+0

Se cargan en la función getView del adaptador. –

0

Ah, comprobar su Logcat asegurarse de que su ISN aplicación no se mata y se reinicia.La mayoría de los teléfonos limitan el tamaño total de la aplicación a 16mb o 24mb. Es fácil cargar un montón de imágenes, ejecutarlas, matarlas, reiniciarlas y que su dispositivo onPause no cargue datos grandes fuera de la pantalla. Es la colección de garabatos de los pobres.

+0

he editado mi pregunta. por favor miren – Praveen

7

Por lo que tengo entendido, debe actualizar su lista una vez que haya finalizado el desplazamiento. Eso es fácil. Aquí está el código fijo para usted:

EfficientAdapter adapter; 

@Override 
public void onCreate(Bundle savedInstanceState) { 
    mIcon1 = BitmapFactory.decodeResource(this.getResources(), 
      R.drawable.icon48x48_1); 
    mIcon2 = BitmapFactory.decodeResource(this.getResources(), 
      R.drawable.icon48x48_2); 
    super.onCreate(savedInstanceState); 
    adapter=new EfficientAdapter(this); 
    setListAdapter(adapter); 
    getListView().setOnScrollListener(this); 
} 

public void onScroll(AbsListView view, int firstVisibleItem, 
    int visibleItemCount, int totalItemCount) { 
} 

public void onScrollStateChanged(AbsListView view, int scrollState) { 
    switch (scrollState) { 
    case OnScrollListener.SCROLL_STATE_IDLE: 
     mBusy = false; 
     adapter.notifyDataSetChanged() 
     break; 
    case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL: 
     mBusy = true; 
     // mStatus.setText("Touch scroll"); 
     break; 
    case OnScrollListener.SCROLL_STATE_FLING: 
     mBusy = true; 
     // mStatus.setText("Fling"); 
     break; 
    } 
} 

notifyDataSetChanged le dirá el adaptador para volver a visualizar todos los elementos visibles, por lo que será visualizado con imagen2.

17

Lo tengo. Este es el código perfecto que quiero. La carga diferida funciona con los adaptadores personalizados que son solo iconos de elementos de la lista visibles. Espero que ayude a los principiantes

public class List14 extends ListActivity implements ListView.OnScrollListener { 
// private TextView mStatus; 

private static boolean mBusy = false; 
static ViewHolder holder; 

public static class EfficientAdapter extends BaseAdapter { 
    private LayoutInflater mInflater; 
    private Bitmap mIcon1; 
    private Bitmap mIcon2; 
    private Context mContext; 

    public EfficientAdapter(Context context) { 
     // Cache the LayoutInflate to avoid asking for a new one each time. 
     mInflater = (LayoutInflater) context 
       .getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
     mContext = context; 
     // Icons bound to the rows. 
     mIcon1 = BitmapFactory.decodeResource(context.getResources(), 
       R.drawable.icon48x48_1); 
     mIcon2 = BitmapFactory.decodeResource(context.getResources(), 
       R.drawable.icon48x48_2); 
    } 

    /** 
    * The number of items in the list is determined by the number of 
    * speeches in our array. 
    * 
    * @see android.widget.ListAdapter#getCount() 
    */ 
    public int getCount() { 
     return DATA.length; 
    } 

    /** 
    * Since the data comes from an array, just returning the index is 
    * sufficent to get at the data. If we were using a more complex data 
    * structure, we would return whatever object represents one row in the 
    * list. 
    * 
    * @see android.widget.ListAdapter#getItem(int) 
    */ 
    public Object getItem(int position) { 
     return position; 
    } 

    /** 
    * Use the array index as a unique id. 
    * 
    * @see android.widget.ListAdapter#getItemId(int) 
    */ 
    public long getItemId(int position) { 
     return position; 
    } 

    /** 
    * Make a view to hold each row. 
    * 
    * @see android.widget.ListAdapter#getView(int, android.view.View, 
    *  android.view.ViewGroup) 
    */ 
    public View getView(int position, View convertView, ViewGroup parent) { 
     // A ViewHolder keeps references to children views to avoid 
     // unneccessary calls 
     // to findViewById() on each row. 

     // When convertView is not null, we can reuse it directly, there is 
     // no need 
     // to reinflate it. We only inflate a new View when the convertView 
     // supplied 
     // by ListView is null. 
     if (convertView == null) { 
      convertView = mInflater.inflate(R.layout.list_item_icon_text, 
        parent, false); 

      // Creates a ViewHolder and store references to the two children 
      // views 
      // we want to bind data to. 
      holder = new ViewHolder(); 
      holder.text = (TextView) convertView.findViewById(R.id.text); 
      holder.icon = (ImageView) convertView.findViewById(R.id.icon); 

      convertView.setTag(holder); 
     } else { 
      // Get the ViewHolder back to get fast access to the TextView 
      // and the ImageView. 
      holder = (ViewHolder) convertView.getTag(); 
     } 

     if (!mBusy) { 

      holder.icon.setImageBitmap(mIcon1); 

      // Null tag means the view has the correct data 
      holder.icon.setTag(null); 

     } else { 
      holder.icon.setImageBitmap(mIcon2); 

      // Non-null tag means the view still needs to load it's data 
      holder.icon.setTag(this); 
     } 
     holder.text.setText(DATA[position]); 

     // Bind the data efficiently with the holder. 
     // holder.text.setText(DATA[position]); 

     return convertView; 
    } 

    static class ViewHolder { 
     TextView text; 
     ImageView icon; 
    } 
} 

private Bitmap mIcon1; 
private Bitmap mIcon2; 

@Override 
public void onCreate(Bundle savedInstanceState) { 
    mIcon1 = BitmapFactory.decodeResource(this.getResources(), 
      R.drawable.icon48x48_1); 
    mIcon2 = BitmapFactory.decodeResource(this.getResources(), 
      R.drawable.icon48x48_2); 
    super.onCreate(savedInstanceState); 
    setListAdapter(new EfficientAdapter(this)); 
    getListView().setOnScrollListener(this); 
} 

public void onScroll(AbsListView view, int firstVisibleItem, 
     int visibleItemCount, int totalItemCount) { 
} 

public void onScrollStateChanged(AbsListView view, int scrollState) { 
    switch (scrollState) { 
    case OnScrollListener.SCROLL_STATE_IDLE: 
     mBusy = false; 

     int first = view.getFirstVisiblePosition(); 
     int count = view.getChildCount(); 

     for (int i = 0; i < count; i++) { 

      holder.icon = (ImageView) view.getChildAt(i).findViewById(
        R.id.icon); 
      if (holder.icon.getTag() != null) { 
       holder.icon.setImageBitmap(IMAGE[first+i]);// this is the image url array. 
       holder.icon.setTag(null); 
      } 
     } 

     // mStatus.setText("Idle"); 
     break; 
    case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL: 
     mBusy = true; 
     // mStatus.setText("Touch scroll"); 
     break; 
    case OnScrollListener.SCROLL_STATE_FLING: 
     mBusy = true; 
     // mStatus.setText("Fling"); 
     break; 
    } 
} 

private static final String[] DATA = { "Abbaye de Belloc", 
     "Abbaye du Mont des Cats", "Abertam", "Abondance", "Ackawi", 
     "Acorn", "Adelost", "Affidelice au Chablis", "Afuega'l Pitu", 
     "Yarra Valley Pyramid", "Yorkshire Blue", "Zamorano", 
     "Zanetti Grana Padano", "Zanetti Parmigiano Reggiano" }; 
    } 
+4

es un poco extraño que uses un soporte y luego siempre uses findViewById ... podrías simplificarlo si evitas ese titular o más rápido si configuras las cosas con la ayuda del titular ... – Karussell