8

我有一個擴展ArrayAdapter的自定義適配器,它實現了視圖持有者模式以顯示來自Web服務的數據(文本+圖像)。Android:當向ArrayAdapter添加數據時可見的ListView圖像閃爍

爲我用的是異步任務模式從Android的開發者網站上的先進的訓練圖像的延遲加載,

我也使用磁盤+ RAM位圖緩存。

當有額外的數據要檢索我添加一個頁腳視圖,點擊它從Web服務檢索額外的數據並將其添加到適配器。

問題是,當這個新數據被添加時,一些可見圖像正在改變並立即改變回來,這導致奇怪的閃爍。

除此之外,一切工作正常,滾動平穩。

據我所知,當添加新數據時刷新可見視圖時,會發生這些圖像更改。

有沒有辦法繞過這種不需要的行爲?

這是階級做下載和管理異步任務

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

}

這是適配器:

私有類PlaceAdapter擴展ArrayAdapter { 最終詮釋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); 
    } 
} 

mImageViewUrlsWeakHashMap<ImageView, String>,一個ImageView和URL之間映射,可以通過檢查被減小,從而冗餘異步任務調用如果ImageView已經顯示所需要的圖像。沒有這個實現,數據變化的所有可見圖像都會發生閃爍。有了這個,它只發生在一些圖像上。

編輯:我試圖消除這個問題的可能原因,首先我試圖完全繞過緩存實施,並從網絡下載每個位圖,然後我試圖用CommonsWare的無盡的適配器包裝我的適配器,並與我得到了相同結果..所以這隻留下了ImageDownloader類和我的適配器儘可能的原因..我完全失去了這一點。

回答

0

編輯: 我有類似的問題,圖像用於快速更改。發生這種情況的原因是

List適配器的getView()方法被多次調用。每次調用getView()時,都會嘗試下載並設置該行上的圖像。在從網絡下載圖像時,列表中爲該圖像請求的行可能已移出屏幕的可見部分,但適配器試圖重新使用該行,並且這導致在該位置的新行上設置先前請求的圖像(以前請求的行位置)。

試試這個辦法 的適配器,帶setTag設置在ImageView的請求的URL:

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

緩存,加載位圖前,檢查是否正確加載圖像:

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只是一個POJO來持有對imageView和圖像的URL的引用

4

這種閃爍的原因是,在列表視圖列表項中被重用。重新使用時,列表項目中的圖像會保留先顯示的舊圖像參考。稍後一旦新圖像下載,它就開始顯示。這會導致閃爍的行爲。 爲避免此閃爍問題,請務必在重新使用時從imageview中清除舊圖像引用。

在你的情況下,添加holder.image.setImageBitmap(null);holder =(ViewHolder)convertView.getTag();

所以,你的getView()方法如下所示:

@覆蓋 公共查看getView(最終詮釋的立場,觀點convertView,ViewGroup以及母公司){

... 

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

解決方法是在沒有更改時不重新加載圖像。

在您的適配器getView()做的事:

// 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 
} 

成功(添加ImageLoadingListener)設置holder.lastImageUrl =路徑,對失敗和取消設置holder.lastImageUrl爲null,這樣它會重新加載下次。

0

您可能會打電話給adapter.notifyDatasetChanged(),導致listview重新加載其列表項。 儘量不要打電話adapter.notifyDatasetChanged() 你的列表視圖應該在你滾動時自動更新。 但這會在滾動時導致ArrayIndexOutOfBounds異常。