l-amoureu Posté(e) 3 mars 2011 Share Posté(e) 3 mars 2011 Bonjour J'ai créé une listView qui contient X item, chaque item comportant une image, et des champs text, tous téléchargé sur le net, et obtenu dans un json (les champs text ainsi que l'url sont dans le Json) Pour empêcher l'attente, j'ai threadé le tout. D'abord, le téléchargement du json, qui une fois fait, associe directement chaque item au text associé. Et, une fois le téléchargement du Json terminé, un nouveau thread est lancé pour télécharger les image a partir de l'url contenu dans le Json (en attendant que le téléchargement et l'association a l'imageView se fasse, je met une image de base) Sauf que je me suis rendu compte que j'ai parfois un probleme d'affectation de l'image (téléchargé dans un thread donc) avec l'item. En faisant un jeu de test (avec des Log) au niveau de l'appel du thread de téléchargement des image, et a l'interieur du thread, je me suis rendu compte que le holder que je lui passe en paramètre est toujours le même, j'ai donc des problème d'affectation au mauvais item private class DownloadFilesTask extends AsyncTask<ViewHolder, Integer, Long> { ViewHolder mHolder; String mImage; @Override protected Long doInBackground(ViewHolder... holders) { try { mHolder = holders[0]; int position = holders[0].position; mImage = vins.get(position).url_small; Bitmap image = telechargerImage(mImage); Log.i("bitmap + holder apres", "bitmap : "+vins.get(position).url_small + " "+mHolder.toString()); bitmaps.remove(mImage); bitmaps.put(mImage, image); } catch (Exception e) { e.printStackTrace(); } return null; } @Override protected void onPostExecute(Long result) { super.onPostExecute(result); try { mHolder.image.setImageBitmap(bitmaps.get(mImage)); } catch (Exception e) { e.printStackTrace(); } } } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder = null; if (convertView == null) { convertView = myInflater.inflate(R.layout.item_vin, null); holder = new ViewHolder(); holder.image = (ImageView) convertView.findViewById(R.id.imagePetiteDuVin); holder.nom = (TextView) convertView.findViewById(R.id.nomDuChateau); holder.appellation = (TextView) convertView.findViewById(R.id.appellationDuVin); holder.prix = (TextView) convertView.findViewById(R.id.prixDuVins); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } try { holder.nom.setText(vins.get(position).nom); holder.appellation.setText(vins.get(position).appellation); holder.prix.setText("Prix : "+vins.get(position).prix); holder.position = position; Bitmap logo = BitmapFactory.decodeResource(mContext.getResources(), mContext.getResources().getIdentifier("com.max:drawable/logo_vinoreco",null,null)); holder.image.setImageBitmap(logo); } catch (Exception e) { e.printStackTrace(); } if (!bitmaps.containsKey(vins.get(position).url_small)){ try { Log.i("bitmap + holder avant", "bitmap : "+vins.get(position).url_small + " "+holder.toString()); new DownloadFilesTask().execute(holder); } catch (Exception e) { e.printStackTrace(); } } else holder.image.setImageBitmap(bitmaps.get(vins.get(position).url_small)); return convertView; } Le problème vient donc du fait (je pense tout du moins) que mon holder possède toujours la même adresse, et vu que je le passe a mon thread qui télécharge puis associe l'image a mon ImageView (imageView qui a pu etre modifié a n'importe quel moment vu que mon holder est toujours le même), l'image est mal affecté. Est ce qu'il y a un moyen d'empecher ca ? (donc, d'avoir une vrai nouvelle instance de mon holder) Cordialement Lien vers le commentaire Partager sur d’autres sites More sharing options...
chpil Posté(e) 3 mars 2011 Share Posté(e) 3 mars 2011 Tu retombes dans le même problème que lorsque tu ne threadais pas le chargement des images: ton Holder est associée à une vue d'un élément, pas à l'élément lui-même, et une instance de Holder est réutilisée pour afficher plusieurs lignes différentes au fil du scrolling de ta ListView. De plus, il serait sans doute préférable de ne pas lancer un nouveau Thread à chaque chargement d'image, mais plutôt d'avoir un seul Thread, qui se met en attente des différents chargements d'image. Tu peux utiliser HandlerThread pour cela, avec un Handler pour communiquer avec Lien vers le commentaire Partager sur d’autres sites More sharing options...
l-amoureu Posté(e) 3 mars 2011 Auteur Share Posté(e) 3 mars 2011 Pour la réutilisation du holder par le systeme, je comprend maintenant. Un truc que je comprend pas trop : la convertView est associé a un seul item de la list ? Ou est-ce comme le holder, c'est réutilisé constamment ? J'essay, vraiment j'essay, mais je ne comprend pas comment, lorsque je lance mon thread, lui dire que le téléchargement de l'image se fera pour un item bien préci. En fait, ce que je comprend pas, c'est que lorsque je lance mon thread, il va lancer exécuter doInBackground, qui télécharge l'image. Et, lorsque le téléchargement est fini, le Bitmap est stocké dans la HashMap, qui sera associé a un String, l'url de l'image. Ensuite, c'est onPostExecute qui prend la main. Le probleme, c'est que je peux pas envoyer directement l'url de doInBackground a onPostExecute pour qu'il puisse récuperer le bon Bitmap, idem pour les valeur contenu dans le holder qui a lancé le thread (pour rappel, le handler contient l'ImageView, et les TextView de l'item). Je suis donc obligé de créé une variable globale a la classe DownloadFilesTask qui sera par exemple le holder de lancement du thread. Mais, c'est juste idiot, parce que ses champs (au holder) peuvent changer a n'importe quel moment vu qu'il est utilisé par d'autre. Je ne vois donc pas du tout comment faire en sorte que mes images téléchargé soit associé au bon ImageView lorsque je fais des thread Lien vers le commentaire Partager sur d’autres sites More sharing options...
chpil Posté(e) 3 mars 2011 Share Posté(e) 3 mars 2011 La convertView qui est passée en paramètre de getView, c'est justement une View déjà instanciée qu'Android souhaite réutiliser pour l'affichage d'un nouvel item de la liste => recyclage Pour ton problème de chargement d'image, il faut que tu sépares le chargement de l'image de l'affichage effectif de l'image. En gardant le fonctionnement par AsyncTask, avec un nouveau thread pour tout nouveau chargement d'image, tu pourrais faire comme ceci: private Set<String> imageUrlsProcessed = new HashSet<String>(); Map<String,Bitmap> bitmaps = new Hashtable<String,Bitmap>(); // Utiliser Hashtable plutot que HashMap, cette dernière n'étant pas synchronisée (problèmes potentiels lors d'accès concurrents) private class DownloadFilesTask extends AsyncTask<String, Void, Void> { private String mImage; @Override protected void doInBackground(String... urls) { try { Bitmap image = telechargerImage(urls[0]); bitmaps.put(urls[0], image); } catch (Exception e) { e.printStackTrace(); } } @Override protected void onPostExecute() { // Notification de la liste du changement du contenu de l'Adapter notifyDataSetChanged(); } } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder = null; if (convertView == null) { convertView = myInflater.inflate(R.layout.item_vin, null); holder = new ViewHolder(); holder.image = (ImageView) convertView.findViewById(R.id.imagePetiteDuVin); holder.nom = (TextView) convertView.findViewById(R.id.nomDuChateau); holder.appellation = (TextView) convertView.findViewById(R.id.appellationDuVin); holder.prix = (TextView) convertView.findViewById(R.id.prixDuVins); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } String imageUrl = vins.get(position).url_small; // Verifier si l'url a déjà été traitée (ie DownloadTask déjà lancée pour cette url ?) if (!imageUrlsProcessed.contains(imageUrl)) { imageUrlsProcessed.add(imageUrl); new DownloadTask(imageUrl).execute(); } try { holder.nom.setText(vins.get(position).nom); holder.appellation.setText(vins.get(position).appellation); holder.prix.setText("Prix : "+vins.get(position).prix); holder.position = position; if (bitmaps.containsKey(imageUrl)){ // Image déjà chargée holder.image.setImageBitmap(bitmaps.get(imageUrl)); } else { // Logo par défaut Bitmap logo = BitmapFactory.decodeResource(mContext.getResources(), mContext.getResources().getIdentifier("com.max:drawable/logo_vinoreco",null,null)); holder.image.setImageBitmap(logo); } } catch (Exception e) { e.printStackTrace(); } return convertView; } L'AysncTask charge l'image, et met le résultat dans la Map, et notifie ensuite (via notifyDataSetChanged) la liste que le contenu de l'adapter a changé, ce qui devrait provoquer le réaffichage de la liste Lors de l'affichage d'un élément, on teste si on n'a pas déjà traité l'url de l'image, et le cas échéant on lance l'AsyncTask pour effectuer le chargement. Et on affiche l'image si elle est déjà chargée, le logo sinon Pour moi, cette solution reste perfectible, car on va potentiellement lancer plusieurs threads en parallèle, il serait préférable de n'avoir qu'un thread qui séquentialise les chargements. Il faudrait utiliser HandlerThread/Handler pour ce faire, mais c'est vrai que c'est un peu plus compliqué Lien vers le commentaire Partager sur d’autres sites More sharing options...
l-amoureu Posté(e) 4 mars 2011 Auteur Share Posté(e) 4 mars 2011 Merci beaucoup pour ta réponse, ton code, en plus de fonctionner, m'a permis je pense de comprendre le fonctionnement d'un adapter, etc ... Donc, merci beaucoup :) Lien vers le commentaire Partager sur d’autres sites More sharing options...
montassar Posté(e) 25 mai 2011 Share Posté(e) 25 mai 2011 slt tt le monde j'ai parser des info id nom... avec la format json via un serveur web php et j'aimerai ajouter dans mon code la partie de parser l'image avec id et nom et prix package com.pxr.tutorial.json; import java.util.ArrayList; import java.util.HashMap; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import android.app.ListActivity; import android.content.Intent; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.AdapterView; import android.widget.Button; import android.widget.ListAdapter; import android.widget.ListView; import android.widget.SimpleAdapter; import android.widget.Toast; import android.widget.AdapterView.OnItemClickListener; import com.pxr.tutorial.xmltest.R; public class Restaurant extends ListActivity { String urlresto= "http://www.monresto.net/partenaires/restaurant/"; //TextView tv; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);; setContentView(R.layout.listplaceholder); Button button = new Button(this); Bundle bundle = getIntent().getExtras(); if (bundle != null) { button.setText("Item name = " + bundle.getString("id") + " --- Go Back "); } else { button.setText("Go Back"); } ArrayList<HashMap<String, String>> mylist = new ArrayList<HashMap<String, String>>(); JSONObject json = JSONfunctions.getJSONfromURL("http://monresto.net/android/marsa.php?id_cp="+ bundle.getString("id")); try{ JSONArray monrestodb1 = json.getJSONArray("monrestodb1"); for(int i=0;i<monrestodb1.length();i++){ HashMap<String, String> map = new HashMap<String, String>(); JSONObject e = monrestodb1.getJSONObject(i); map.put("name", String.valueOf(i)); map.put("name", "" + e.getString("nom")); map.put("id", "" + e.getString("id")); //map.put("statut", "" + e.getString("statut")); //map.put("magnitude", "Magnitude: " + e.getString("magnitude")); mylist.add(map); } }catch(JSONException e) { Log.e("log_tag", "Error parsing data "+e.toString()); } ListAdapter adapter = new SimpleAdapter(this, mylist , R.layout.listmontplaisir, new String[] { "name", "magnitude" }, new int[] { R.id.montplaisirtext }); setListAdapter(adapter); final ListView lv = getListView(); lv.setTextFilterEnabled(true); lv.setOnItemClickListener(new OnItemClickListener() { public void onItemClick(AdapterView<?> parent, View view, int position, long id) { @SuppressWarnings("unchecked") HashMap<String, String> o = (HashMap<String, String>) lv.getItemAtPosition(position); Toast.makeText(Restaurant.this, "NAME '" + o.get("id") + "' was clicked.", Toast.LENGTH_SHORT).show(); Intent intent = new Intent(Restaurant.this, Famille_plat.class); //intent.putExtra("url", "http://monresto.net/android/marsa.php"); String txt = o.get("id"); intent.putExtra("id", txt); startActivity(intent); } }); } } svp aider moi Lien vers le commentaire Partager sur d’autres sites More sharing options...
Recommended Posts
Archivé
Ce sujet est désormais archivé et ne peut plus recevoir de nouvelles réponses.