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