閱讀995 返回首頁    go 技術社區[雲棲]


ListView異步加載圖片(解決圖片混淆)

代碼下載地址:
https://115.com/file/e75ks6jj#ImageLoader_test.zip




       由於工作原因,很久沒有寫博客了,工作中經常遇到ListView異步加載圖片的問題,國內的網站上查了N多資料,幾乎沒有一個可用的,最根本的圖片混淆問題都沒有得到充分地解決。我的這個例子是借鑒Google Code中的例子,刪除了其中的沒有必要的代碼,完全可行。
 
該工程由ImageListActivity、ImageAdapter、ImageDownloader三個類構成。前兩個類比較簡單,這裏不再贅述,下麵我們就來分析一下ImageDownloader究竟是如何做到避免圖片混淆的。
 
    public void download(String url, ImageView imageView) {
        resetPurgeTimer(); //清空集合
        Bitmap bitmap = getBitmapFromCache(url);
 
        if (bitmap == null) {
            forceDownload(url, imageView);
        } else {
            cancelPotentialDownload(url, imageView);
            imageView.setImageBitmap(bitmap);
        }
}
 
通過研究以上代碼可知,真正的下載代碼是由forceDownload方法來完成,該方法如下:
 
private void forceDownload(String url, ImageView imageView) {
    // State sanity: url is guaranteed to never be null in 
     //  DownloadedDrawable and cache keys.
    if (url == null) {
         imageView.setImageDrawable(null);
         return;
    }
 
if (cancelPotentialDownload(url, imageView)) {
BitmapDownloaderTask task = new BitmapDownloaderTask(imageView)
DownloadedDrawable downloadedDrawable = 
                        new DownloadedDrawable(task);
imageView.setImageDrawable(downloadedDrawable);
task.execute(url);
}
 
 
}
用便於理解的語言可以這樣解釋:
if(取消了之前該imageView對應的圖片下載) {
       1.創建下載圖片的Task:BitmapDownloaderTask。
         (讓BitmapDownloaderTask 擁有imageView的引用,實現二者之           間的綁定,既該imageView一一對應一個BitmapDownloaderTask 對象)
 
       2.初始化每一個ImageView為默認圖片或顏色。(該默認圖片就是DownloadedDrawable,該DownloadedDrawable擁有BitmapDownloaderTask 的引用,實現二者之間的綁定,既該DownloadedDrawable一一對應一個BitmapDownloaderTask 對象)
 
       3.啟動下載任務
}
 
有些童鞋對cancelPotentialDownload可能理解的不是很透徹。
   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 {
                // The same URL is already being downloaded.
                return false;
            }
        }
           
        return true;
}
 
該方法對於,剛剛進入頁麵在沒有做任何操作(尤其是滑動)時或者說對於listview中任何一個item首次加載時,當然是返回true,因為這個時候bitmapDownloaderTask為null.這時就會首次去執行下載圖片的任務。
但是當我們向下滑動一屏,再向上滑動回之前那一屏時,這個時候bitmapDownloaderTask 已不再為null. 我們將bitmapDownloaderTask.url與參數中的url進行對比(這個時候注意:很明顯參數中的url才是我們需要下載的url):如果不等,則停止正在下載的(因為這不是我們需要的),返回true;如果相等,則返回false,繼續當前的圖片下載任務。


 
 
這時候,又有童鞋問了,為什麼以上imageView對應的url會變呢?
別著急,上下滑動listview,請查看ImageAdapter類中getView方法打印出來的Log, 如下圖:






再看看getView方法:
public View getView(int position, View view, ViewGroup parent) { 
if (view == null) { 
view = new ImageView(parent.getContext());
view.setPadding(6, 6, 6, 6); 
view.setMinimumHeight(150); 
view.setMinimumWidth(150); 
Log.v(TAG, "getView, ==========new========pos: "  
           + position + " ,view: " + view); 
} else { 
Log.v(TAG, "getView, pos: " + position + " ,view: " + view); 
}
 
imageDownloader.download(URLS[position], (ImageView) view); 
return view; 
}


怎麼樣,觀察標記,有什麼想法沒。這說明當進入listView頁麵時,如手機一屏隻能顯示6個item,那麼android係統就初始化6個view,當上下滑動時,android係統會重用這些已經創建好的ImageView,改變的僅僅是ImageView所顯示的圖片。


下麵我們就對第一屏的最頂上的一個ImageView(以下取名為A)進行分析:
我們在第一屏首次構建了A,並綁定了一個BitmapDownloaderTask,當用手指下滑至A消失時,這時肯定會露出一個新的item,該item就會重用之前的A。這時,如果取出之前消失的A對應的task,我們對比task.url與參數中的url,如果不等,那麼就暫停沒有消失時正在下載的url(因為已經滑過去了,既不可見,再下載就沒有意義了,再說優先級更高的應該是當前可見部分圖片的下載)。


之後我們又調用了
DownloadedDrawable downloadedDrawable = new DownloadedDrawable(task);
imageView.setImageDrawable(downloadedDrawable);
在下載圖片之前,需要先給ImageView設置默認的圖片.以上兩句很重要,否則就不能做到真正的圖片混淆。


我查了很多資料,國內資料上做的最好的也就是該例子去掉以上兩句的效果,會先閃一下錯誤的圖片,而後再顯示正確的圖片。


以下是我收錄的比較好的更新ListView的文章,在這裏分享給大家:
 
1. code google(最好的實現實例)
2. Android ListView 異步加載圖片 再優化
3. 滑動過程中不加載圖片
4. 更新ListView的一個Item

最後更新:2017-04-02 22:14:28

  上一篇:go 一位女上司寫給自己下屬的信
  下一篇:go 大學,不需要愛情