8

Ho un adattatore personalizzato che estende ArrayAdapter, implementa i modelli di supporto per visualizzare i dati (testo + immagine) da un servizio web.Android: sfarfallio visibile delle immagini di ListView quando si aggiungono dati a ArrayAdapter

per lazy loading delle immagini che uso il modello compiti asincroni dalla formazione avanzata nel sito sviluppatori di Android,

Io uso anche la cache bitmap del disco + ram.

quando ci sono dati aggiuntivi da recuperare, aggiungo una vista a piè di pagina che facendo clic su di essa recupera dati aggiuntivi dal servizio web e la aggiunge all'adattatore.

il problema è che quando si aggiungono questi nuovi dati, alcune delle immagini visibili cambiano e si modificano immediatamente, provocando uno strano sfarfallio.

diverso da quello tutto funziona correttamente e lo scorrimento è scorrevole.

Per quanto ho capito, queste modifiche delle immagini si verificano quando le viste visibili vengono aggiornate quando vengono aggiunti nuovi dati.

esiste un modo per aggirare questo comportamento indesiderato?

Questa è la classe a fare il download e la gestione dei compiti asincroni

public class ImageDownloader { 
private ImageCache mCache; 
private int reqWidth; 
private int reqHeight; 

public void download(String url, ImageView imageView, ImageCache imageCache, int reqHeight, int reqWidth) { 
    mCache = imageCache; 
    this.reqHeight = reqHeight; 
    this.reqWidth = reqWidth; 

    if (cancelPotentialDownload(url, imageView)) { 
     BitmapDownloaderTask task = new BitmapDownloaderTask(imageView); 

     DownloadedDrawable downloadedDrawable = new DownloadedDrawable(task); 
     imageView.setImageDrawable(downloadedDrawable); 
     task.execute(url); 
    } 
} 

private class BitmapDownloaderTask extends AsyncTask<String, Void, Bitmap> { 
    private String url; 
    private WeakReference<ImageView> imageViewReference; 

    public BitmapDownloaderTask(ImageView imageView) { 
     imageViewReference = new WeakReference<ImageView>(imageView); 
    } 

    @Override 
    protected Bitmap doInBackground(String... strings) { 
     Bitmap bitmap = null; 
     try { 
      bitmap = mCache.getBitmapFromURL(strings[0], reqWidth, reqHeight); 
     } catch (IOException e) { 
     } 

     return bitmap; 
    } 

    @Override 
    protected void onPostExecute(Bitmap bitmap) { 
     if (isCancelled()) bitmap = null; 

     if (imageViewReference != null) { 
      ImageView imageView = imageViewReference.get(); 
      BitmapDownloaderTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView); 

      if (imageView != null) { 
       imageView.setImageBitmap(bitmap); 
      } 
     } 
    } 
} 

private static class DownloadedDrawable extends ColorDrawable { 
    private WeakReference<BitmapDownloaderTask> bitmapDownloaderTaskReference; 

    public DownloadedDrawable(BitmapDownloaderTask bitmapDownloaderTask) { 
     bitmapDownloaderTaskReference = new WeakReference<BitmapDownloaderTask>(bitmapDownloaderTask); 
    } 

    public BitmapDownloaderTask getBitmapDownloaderTask() { 
     return bitmapDownloaderTaskReference.get(); 
    } 
} 

private static BitmapDownloaderTask getBitmapDownloaderTask(ImageView imageView) { 
    if (imageView != null) { 
     Drawable drawable = imageView.getDrawable(); 
     if (drawable instanceof DownloadedDrawable) { 
      DownloadedDrawable downloadedDrawable = (DownloadedDrawable)drawable; 
      return downloadedDrawable.getBitmapDownloaderTask(); 
     } 
    } 
    return null; 
} 

private static boolean cancelPotentialDownload(String url, ImageView imageView) { 
    BitmapDownloaderTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView); 

    if (bitmapDownloaderTask != null) { 
     String bitmapUrl = bitmapDownloaderTask.url; 
     if ((bitmapUrl == null) || (!bitmapUrl.equals(url))) { 
      bitmapDownloaderTask.cancel(true); 
     } else { 
      return false; 
     } 
    } 
    return true; 
} 

}

questo è l'adattatore:

classe privata PlaceAdapter estende ArrayAdapter { final int viewResourceId;

public PlaceAdapter(Context context, int textViewResourceId, List<PlaceModel> objects) { 
     super(context, textViewResourceId, objects); 
     viewResourceId = textViewResourceId; 
    } 

    @Override 
    public View getView(final int position, View convertView, ViewGroup parent) { 
     ViewHolder holder; 

     if (convertView == null) { 
      LayoutInflater inflater = getLayoutInflater(); 
      convertView = inflater.inflate(viewResourceId, null); 

      holder = new ViewHolder(convertView); 
      convertView.setTag(holder); 
     } else { 
      holder = (ViewHolder) convertView.getTag(); 
     } 

     PlaceModel place = getItem(position); 

     holder.name.setText(place.getName()); 
     holder.address.setText(place.getVicinity()); 
     holder.position = position; 

     if (place.getIcon() != null) { 
      String url = mImageViewUrls.get(holder.image); 
      if (url == null || (url != null && !url.equals(place.getIcon()))) { 
       mDownloader.download(place.getIcon(), holder.image, mCache, 100, 100); 
       mImageViewUrls.put(holder.image, place.getIcon()); 
      } 
     } else { 
      holder.image.setImageBitmap(null); 
      mImageViewUrls.remove(holder.image); 
     } 

     return convertView; 
    } 
} 

private class ViewHolder { 
    public final ImageView image; 
    public final TextView name; 
    public final TextView address; 
    public int position; 

    public ViewHolder(View row) { 
     image = (ImageView) row.findViewById(R.id.placeRow_imageView); 
     name = (TextView) row.findViewById(R.id.placeRow_placeName); 
     address = (TextView) row.findViewById(R.id.placeRow_placeAddress); 
    } 
} 

mImageViewUrls è un WeakHashMap<ImageView, String> che mappa tra un ImageView e un URL, quindi ridondanti compiti asincroni invocazioni possono essere ridotti controllando se il ImageView sta già mostrando l'immagine desiderata. senza questa implementazione, lo sfarfallio sta accadendo in tutte le immagini visibili sul cambio di dati. con questo, succede solo con alcune immagini.

EDIT: Ho cercato di eliminare le possibili cause di questo problema, prima ho provato a bypassare completamente l'implementazione della cache e scaricare ogni bitmap dalla rete, poi ho provato ad avvolgere il mio adattatore con l'adattatore Endless di CommonsWare e con entrambi ho avuto lo stesso risultato .. quindi questo lascia solo la classe ImageDownloader e il mio adattatore come possibili cause .. Sono completamente perso su questo.

risposta

0

Modifica: Ho avuto un problema simile, le immagini utilizzate per cambiare rapidamente. Ciò accade perché

il metodo getView() dell'elenco di schede viene chiamato più volte. ogni volta che viene chiamato getView(), si tenta di scaricare e impostare l'immagine sulla riga. Nel momento in cui l'immagine viene scaricata dalla rete, la riga nell'elenco richiesto per quell'immagine potrebbe essere stata spostata dalla parte visibile dello schermo ma l'adattatore prova a riutilizzare quella riga e questo porta all'impostazione dell'immagine precedentemente richiesta sulla nuova riga in quella posizione (posizione della riga richiesta in precedenza).

Prova questo approccio l'adattatore, impostare l'URL richiesto sul ImageView con setTag:

if(item.get_referenceImage().length()!=0){ 
    //with view, hold the url. This is used to verify that right image is loaded on download completion 
    holder.refImageView.setTag(item.get_referenceImage()); 
    imageManager.loadImage(new MetaItem(item.get_referenceImage(), holder.refImageView)); 
}else{ 
    //no image, set default 
    Log.d(TAG, "No images found for the view setting default image"); 
    holder.refImageView.setTag(null); //this is req, otherwise image cache will retain previous image 
    holder.refImageView.setImageResource(R.drawable.blank_96_1382x); 
} 

nella cache, prima di caricare il bitmap, verificare se l'immagine a destra è caricato:

String url = (String) m_imageViewRef.get().getTag(); 
    if(url != null && url.compareTo(m_metaItem.m_url) == 0){ 
     m_metaItem.m_imageViewRef.get().setImageBitmap(result); 
    } 

PS: MetaItem è solo un POJO per contenere il riferimento a imageView e l'URL dell'immagine

4

Il motivo di questo sfarfallio è che, nella lista degli elenchi gli oggetti sono riutilizzati. Quando riutilizzate, le visualizzazioni di immagini nella voce di elenco conservano il vecchio riferimento dell'immagine che viene visualizzato per primo. Successivamente, una volta scaricata la nuova immagine, inizia a mostrare. questo causa il comportamento tremolante. Per evitare questo problema di sfarfallio, deselezionare sempre il vecchio riferimento dell'immagine dalla visualizzazione di immagini quando viene riutilizzata.

Nel tuo caso, aggiungi holder.image.setImageBitmap (null); dopo holder = (ViewHolder) convertView.getTag();

Così, il metodo vostra GetView() sarà simile:

@Override pubblica Vista GetView (posizione finale int, Vista convertView, ViewGroup genitore) {

... 

if (convertView == null) { 
    LayoutInflater inflater = getLayoutInflater(); 
    convertView = inflater.inflate(viewResourceId, null); 

    holder = new ViewHolder(convertView); 
    convertView.setTag(holder); 
} else { 
    holder = (ViewHolder) convertView.getTag(); 
    holder.image.setImageBitmap(null) 
} 

... 

return convertView; 

}

0

La soluzione è di non ricaricare la tua immagine quando non è cambiata.

In adattatori GetView() DO:

// schedule rendering: 
final String path = ... (set path here); 
if (holder.lastImageUrl == null || !holder.lastImageUrl.equals(path) 
       || holder.headerImageView.getDrawable() == null) { 
    // refresh image 
    imageLoader.displayImage(imageUri, imageAware); 
} else { 
    // do nothing, image did not change and does not need to be updated 
} 

in caso di successo (aggiungere un ImageLoadingListener) si imposta holder.lastImageUrl = percorso, sulla fallire e annullare si imposta holder.lastImageUrl su null in modo che ricaricherà la prossima volta.

0

Si potrebbe chiamare adapter.notifyDatasetChanged() che causa listview per ricaricare i suoi listitems. prova a non chiamare adapter.notifyDatasetChanged() il tuo listview dovrebbe aggiornarsi automaticamente quando si scorre. ma questo può causare un'eccezione ArrayIndexOutOfBounds durante lo scorrimento.

Problemi correlati