Lazy Load images sur Listview dans Android (niveau débutant)?

Duplication possible:
Android – Comment faire un chargement paresseux d’images dans ListView

Je travaille sur la liste avec l’adaptateur personnalisé. Je veux charger les images et la vue du texte dessus. Les images sont chargées à partir des URL d’Internet. Je veux juste montrer les images qui sont des éléments de liste visibles à l’utilisateur. J’ai cité l’ exemple du projet opensource Shelves de romainguy , mais il est compliqué de comprendre le code. Pour un niveau débutant, je veux juste savoir comment gérer le tag entre l’adaptateur et l’activité. À partir de l’ exemple commonsware, je peux définir la balise sur l’adaptateur, mais je ne peux pas afficher l’image correspondante à l’état de veille de la vue de liste. S’il vous plaît, aidez-moi avec vos idées. Les codes échantillons sont plus compréhensibles.

Merci.

MODIFIER:

La combinaison de l’adaptateur efficace et lent dans ApiDemos est plus utile à comprendre.

les modifications effectuées sur l’exemple d’adaptateur efficace comme ceci:

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

Cela fonctionne très bien maintenant. Mais lorsque le défilement indique qu’il ne recharge pas correctement l’image. un certain intervalle d’éléments ne montre pas les images2. c’est le bon ordre de chargement des images. Mais pas dans tous les éléments de la liste. Non-concordance entre les intervalles d’éléments solides. comment le corriger?

Praveen –

Comme vous avez déjà trouvé mon article sur ce sujet, je voulais juste le renvoyer à Stackoverflow pour que d’autres puissent l’utiliser.

Voici la discussion de base: http://ballardhack.wordpress.com/2010/04/05/loading-remote-images-in-a-listview-on-android/

Et il y a une classe que j’ai documentée plus tard qui utilise un thread et un callback pour charger les images:

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

Mise à jour: Pour traiter votre exception spécifique, je pense que la vue renvoyée dans la liste à partir de getChildAt n’est pas une image – c’est la vue de mise en page que vous utilisez pour contenir l’image et le texte.

Mise à jour pour inclure le code correspondant : (recommandation de Per @ george-stocker)

Voici l’adaptateur que j’utilisais:

 public class MediaItemAdapter extends ArrayAdapter { private final static Ssortingng TAG = "MediaItemAdapter"; private int resourceId = 0; private LayoutInflater inflater; private Context context; private ImageThreadLoader imageLoader = new ImageThreadLoader(); public MediaItemAdapter(Context context, int resourceId, List 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; } } 

Je l’ai. C’est le code parfait que je veux. Le chargement paresseux fonctionne sur l’adaptateur personnalisé, il suffit de voir les icons des éléments de la liste. J’espère que ça aide les débutants

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

Si j’ai bien compris, vous devez mettre à jour votre liste une fois le défilement terminé. C’est facile. Voici le code fixe pour vous:

 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 indiquera à l’adaptateur de ré-afficher tous les éléments visibles afin qu’ils s’affichent avec image2.

Pour autant que je ViewHolder , le ViewHolder statique ViewHolder en rien. Essayez de placer l’intégralité de la fonction onScrollStateChanged entre /* et */ , en supprimant la ligne static ViewHolder et en modifiant holder = new ViewHolder(); to ViewHolder holder = new ViewHolder(); .

Ah, vérifiez votre logcat pour vous assurer que votre application n’est ni mise à mort ni redémarrée. La plupart des combinés limitent la taille totale de votre application à 16 Mo ou 24 Mo. Il est facile de charger un tas d’images, de s’enfoncer, de se faire tuer, de redémarrer et de ne pas charger le big data hors écran. C’est la collection de garabage du pauvre homme.