Android ListView圖片異步加載顯示
模擬android中的消息機制實現圖片的異步加載和動態顯示
package cn.jd3g.utils;import java.lang.ref.SoftReference; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map.Entry;
import android.graphics.Bitmap; import android.os.Handler; import android.util.Log; import android.widget.ImageView;
/**
- 利用多線程異步加載圖片并更新視圖
- @author xfzhang
*/ public final class AsynImageLoader {
private LoaderThread thread;// 加載圖片并發消息通知更新界面的線程 private HashMap<String, SoftReference<Bitmap>> imageCache;// 圖片對象緩存,key:圖片的url private Handler handler;// 界面Activity的Handler對象
public AsynImageLoader(Handler handler) {
imageCache = new HashMap<String, SoftReference<Bitmap>>(); this.handler = handler;
}
/**
- 加載圖片前顯示到指定的ImageView中,圖片的url保存在視圖對象的Tag中
- @param imageView
- 要顯示圖片的視圖
- @param defaultBitmap
加載需要顯示的提示正在加載的默認圖片對象 */ public void loadBitmap(ImageView imageView, Bitmap defaultBitmap) { // 圖片所對應的url,這個值在加載圖片過程中很可能會被改變 String url = (String) imageView.getTag(); if (imageCache.containsKey(url)) {// 判斷緩存中是否有 SoftReference<Bitmap> softReference = imageCache.get(url); Bitmap bitmap = softReference.get(); if (bitmap != null) {// 如果圖片對象不為空,則可掛接更新視圖,并返回 imageView.setImageBitmap(bitmap); return; } else {// 如果為空,需要將其從緩存中刪除(其bitmap對象已被回收釋放,需要重新加載) Log.e("TAG", "cache bitmap is null"); imageCache.remove(url); } } imageView.setImageBitmap(defaultBitmap);// 先顯示一個提示正在加載的圖片 if (thread == null) {// 加載線程不存在,線程還未啟動,需要新建線程并啟動 thread = new LoaderThread(imageView, url); thread.start(); } else {// 如果存在,就調用線程對象去加載 thread.load(imageView, url); }
}
/**
釋放緩存中所有的Bitmap對象,并將緩存清空 */ public void releaseBitmapCache() { if (imageCache != null) {
for (Entry<String, SoftReference<Bitmap>> entry : imageCache.entrySet()) { Bitmap bitmap = entry.getValue().get(); if (bitmap != null) { bitmap.recycle();// 釋放bitmap對象 } } imageCache.clear();
} }
/**
加載圖片并顯示的線程 */ private class LoaderThread extends Thread {
LinkedHashMap<String, ImageView> mTaskMap;// 需要加載圖片并顯示的圖片視圖對象任務鏈 private boolean mIsWait;// 標識是線程是否處于等待狀態
public LoaderThread(ImageView imageView, String url) {
mTaskMap = new LinkedHashMap<String, ImageView>(); mTaskMap.put(url, imageView);
}
/**
- 處理某個視圖的更新顯示
@param imageView */ public void load(ImageView imageView, String url) { mTaskMap.remove(imageView);// 任務鏈中可能有,得先刪除 mTaskMap.put(url, imageView);// 將其添加到任務中 if (mIsWait) {// 如果線程此時處于等待得喚醒線程去處理任務隊列中待處理的任務
synchronized (this) {// 調用對象的notify()時必須同步 this.notify(); }
} }
@Override public void run() { while (mTaskMap.size() > 0) {// 當隊列中有數據時線程就要一直運行,一旦進入就要保證其不會跳出循環
mIsWait = false; final String url = mTaskMap.keySet().iterator().next(); final ImageView imageView = mTaskMap.remove(url); if (imageView.getTag() == url) {// 判斷視圖有沒有復用(一旦ImageView被復用,其tag值就會修改變) final Bitmap bitmap = MyConnection.getBitmapByUrl(url);// 此方法應該是從網絡或sd卡中加載 try { Thread.sleep(1000);// 模擬網絡加載數據時間 } catch (InterruptedException e1) { e1.printStackTrace(); } // 將加載的圖片放入緩存map中 imageCache.put(url, new SoftReference<Bitmap>(bitmap)); if (url == imageView.getTag()) {// 再次判斷視圖有沒有復用 handler.post(new Runnable() {// 通過消息機制在主線程中更新UI @Override public void run() { imageView.setImageBitmap(bitmap); } }); } } if (mTaskMap.isEmpty()) {// 當任務隊列中沒有待處理的任務時,線程進入等待狀態 try { mIsWait = true;// 標識線程的狀態,必須在wait()方法之前 synchronized (this) { this.wait();// 保用線程進入等待狀態,直到有新的任務被加入時通知喚醒 } } catch (InterruptedException e) { e.printStackTrace(); } }
} } } }</pre>
private class ProductListAdapter extends BaseAdapter {
private AsynImageLoader mImageAsynLoader;
public ProductListAdapter() { mImageAsynLoader = new AsynImageLoader(mHandler); }
@Override public int getCount() { int size = Math.min(mLastItemViewIndex + 1, mDataList.size()); mLastItemViewIndex = size - 1; return size; }
@Override public Object getItem(int position) { return mDataList.get(position); }
@Override public long getItemId(int position) { return position; }
@Override public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) {
convertView = getLayoutInflater().inflate(R.layout.product_list_item, null);
} ImageView imageView = (ImageView) convertView
.findViewById(R.id.iv_item_product_image);
Map<String, String> map = mDataList.get(position); //存放圖片所對應的url imageView.setTag(map.get("product_pic_address")); mImageAsynLoader.loadBitmap(imageView, mDefautBitmap); return convertView; } }</pre>