2009-09-11 76 views
6

這是一種非常常見的情況:在必須從Internet下載的ListView中顯示圖像。Android - 將圖像延遲加載到ListView中的問題

現在我有一個ArrayAdapter的自定義子類,它用於ListView。在ArrayAdapter的getView()實現中,我生成了一個單獨的線程來加載圖像。加載完成後,它會查找合適的ImageView並使用ImageView.setImageDrawable()設置圖像。所以我使用的解決方案有點類似於這個:Lazy load of images in ListView

我遇到的問題是,只要我調用ImageView上的setImageDrawable(),ListView以某種方式刷新所有當前可見行列表!這導致一種無限循環的:

  1. getView()被調用
  2. 線程派生加載圖像
  3. 圖像被加載; setImageDrawable()調用ImageView的
  4. ListView控件將它拾起出於某種原因,並刷新自身
  5. 對於ListView刷新,getView()被調用每個可見行,讓我們回到步驟1,整個事情重複自己

因此,據我所見,解決方案中提出的「Android - 我如何在ListView中執行延遲加載的圖像」(請參閱​​上面的鏈接)根本不起作用。它可能看起來像它,但它會運行得非常慢,因爲在後臺,它會不斷重新加載當前可見的行。

有沒有人遇到過和/或有解決方案嗎?

回答

2

在鏈接的解決方案中,fetchDrawableOnThread()只應在視圖尚未具有正確的可繪製項的情況下才能調用。

如果getDrawable()返回null,則視圖不具有可繪製的。

如果您重複使用插槽,您認爲您需要進一步管理狀態。例如,如果你的視圖有一個存儲URL的成員變量,並且有一個布爾值來表示它是否被加載,那麼很容易知道是否調用fetchDrawableOnThread()

我推測可繪製的toString()詳細說明了圖像加載的路徑。 (如果沒有,你可以將返回的drawable子類化)。在這種情況下,您可以避免上面概述的布爾值,只是做一個比較來確定它是否可以右鍵繪製或者是否獲取替換。

此外,您的getView()在可見行上應確保那些不再可見的被卸載,以防止內存耗盡。一個技巧就是將不再可見的圖像移動到軟引用(因此當需要內存時它們被卸載),作爲另一張原始線程上的海報。

+1

是的我正在使用地圖來緩存圖像。但是,這並不重要,因爲我最終仍然調用setImageDrawable(),它再次觸發刷新。如果我能以某種方式禁用refesh它會解決我的問題。 我不使用SoftReferences(但我會),但這只是一個內存優化,這不能解決無限循環 – 2009-09-11 09:00:53

+0

好點我誤解了你的意思是'線程被派生到*加載*圖像' 。我會重寫我的回答 – Will 2009-09-11 09:03:58

+0

感謝您的快速回復:)當您不重複使用視圖來顯示像我一樣的行時,您現在描述的內容確實解決了問題。重用行我的意思是使用getView()給出的「convertView」參數。在重新使用行視圖時,你是否知道這個解決方案? (因爲在這種情況下,你必須每次都調用setImageDrawable()) – 2009-09-11 09:25:07

3

我用下面的鏈接代碼:another stackoverflow question

我爲了解決回收視圖的疑難問題。一些小的修改設定圖像的URL適配器標籤的ImageView的。以下代碼包含解決回收問題的解決方案:

public void fetchDrawableOnThread(final String urlString, final ImageView imageView,Drawable drw) { 

    imageView.setImageDrawable(drw);//drw is default image 
    if (drawableMap.containsKey(urlString)) { 
     if(imageView.getTag().toString().equals(urlString)) 
     { 
      imageView.setImageBitmap(drawableMap.get(urlString)); 
      imageView.invalidate(); 
      return; 
     } 

    } 

    final Handler handler = new Handler() { 
     @Override 
     public void handleMessage(Message message) { 
      BitmapWrapper wrapper = (BitmapWrapper)message.obj; 
      if(wrapper.imageurl.equals(imageView.getTag().toString())) 
      { 
       imageView.setImageBitmap((Bitmap)wrapper.bitmap); 
       imageView.invalidate(); 
      } 

     } 
    }; 

    Thread thread = new Thread() { 
     @Override 
     public void run() { 
      //TODO : set imageView to a "pending" image 

      Bitmap drawable = fetchDrawable(urlString); 
      BitmapWrapper wrapper = new BitmapWrapper(); 
      wrapper.bitmap = drawable; 
      wrapper.imageurl = urlString; 
      Message message = handler.obtainMessage(1, wrapper); 
      handler.sendMessage(message); 
     } 
    }; 
    thread.start(); 
} 


    public class BitmapWrapper 
{ 
    public Bitmap bitmap; 
    public String imageurl; 
} 
3

我有同樣的問題。

經過近2天重調試/優化,並試圖弄清楚,爲什麼在連續使用setImageBitmap()當我getView()被要求的所有意見一遍又一遍,我想出了一個骯髒的解決方案:

1)擴展的自定義ImageView,你在你的名單

2)在此ImageView使用的所有圖像覆蓋的方法

@Override 
public void requestLayout() 
{ 
    return; 
} 

3)髒,但對我來說,它的作品

4)利潤;)

+1

當列表中的圖像大小相同並且你的圖像視圖也是固定大小(不包含內容)時,可以使用它。這幫了我,謝謝:) – Mark 2013-02-15 09:41:47