手機客戶端以列表形式展示數據是非常常見的一種方式。然而列表中要顯示圖片(比如:頭像)就要採用異步線程加載的方式,這樣做是爲了防止加載圖片數據的時候,花費時間過長,阻塞UI線程,從而達到保持App的流暢性的目的。
下面我將分享 OSChina.NET Android版客戶端的列表異步線程加載圖片的方法:
圖片緩存
private static HashMap<String, SoftReference<Bitmap>> cache;
圖片緩存是當有加載過相同的圖片的時候,可以快速重複使用,比如同一個人的頭像。
圖片控件集合
private static Map<ImageView, String> p_w_picpathViews;
圖片控件集合是一個Map,記錄當前ImageView控件對應的圖片地址,用來防止異步線程加載圖片時候ImageView控件顯示的圖片與實際圖片地址對應的圖片不符,出現錯亂。
線程池
private static ExecutorService pool;
固定線程池裏的併發線程數,可以防止用戶在快速滑動列表的時候,不執行已經滑過去的加載線程。
具體的初始化代碼:
static { cache = new HashMap<String, SoftReference<Bitmap>>(); pool = Executors.newFixedThreadPool(5); //固定線程池 p_w_picpathViews = Collections.synchronizedMap(new WeakHashMap<ImageView, String>()); }
接下來,我們來看看具體是如何加載圖片的:
public void loadBitmap(String url, ImageView p_w_picpathView, Bitmap defaultBmp, int width, int height) { p_w_picpathViews.put(p_w_picpathView, url); Bitmap bitmap = getBitmapFromCache(url); if (bitmap != null) { //顯示緩存圖片 p_w_picpathView.setImageBitmap(bitmap); } else { //加載SD卡中的圖片緩存 String filename = FileUtils.getFileName(url); String filepath = p_w_picpathView.getContext().getFilesDir() + File.separator + filename; File file = new File(filepath); if(file.exists()){ //顯示SD卡中的圖片緩存 Bitmap bmp = ImageUtils.getBitmap(p_w_picpathView.getContext(), filename); p_w_picpathView.setImageBitmap(bmp); }else{ //線程加載網絡圖片 p_w_picpathView.setImageBitmap(defaultBmp); queueJob(url, p_w_picpathView, width, height); } } }
上面的代碼中,我們根據圖片的url地址,先從圖片緩存裏面查找是否已緩存過,如果沒有,再從SD卡的圖片緩存文件中查找,如果再沒有,最後纔是加載網絡圖片。這纔是以最快速的方式顯示圖片。
下面,我將貼出完整的代碼:
/** * 異步線程加載圖片工具類 * @author liux */ public class BitmapManager { private static HashMap<String, SoftReference<Bitmap>> cache; private static ExecutorService pool; private static Map<ImageView, String> p_w_picpathViews; private Bitmap defaultBmp; static { cache = new HashMap<String, SoftReference<Bitmap>>(); pool = Executors.newFixedThreadPool(5); //固定線程池 p_w_picpathViews = Collections.synchronizedMap(new WeakHashMap<ImageView, String>()); } public BitmapManager(){} public BitmapManager(Bitmap def) { this.defaultBmp = def; } /** * 設置默認圖片 * @param bmp */ public void setDefaultBmp(Bitmap bmp) { defaultBmp = bmp; } /** * 加載圖片 * @param url * @param p_w_picpathView */ public void loadBitmap(String url, ImageView p_w_picpathView) { loadBitmap(url, p_w_picpathView, this.defaultBmp, 0, 0); } /** * 加載圖片-可設置加載失敗後顯示的默認圖片 * @param url * @param p_w_picpathView * @param defaultBmp */ public void loadBitmap(String url, ImageView p_w_picpathView, Bitmap defaultBmp) { loadBitmap(url, p_w_picpathView, defaultBmp, 0, 0); } /** * 加載圖片-可指定顯示圖片的高寬 * @param url * @param p_w_picpathView * @param width * @param height */ public void loadBitmap(String url, ImageView p_w_picpathView, Bitmap defaultBmp, int width, int height) { p_w_picpathViews.put(p_w_picpathView, url); Bitmap bitmap = getBitmapFromCache(url); if (bitmap != null) { //顯示緩存圖片 p_w_picpathView.setImageBitmap(bitmap); } else { //加載SD卡中的圖片緩存 String filename = FileUtils.getFileName(url); String filepath = p_w_picpathView.getContext().getFilesDir() + File.separator + filename; File file = new File(filepath); if(file.exists()){ //顯示SD卡中的圖片緩存 Bitmap bmp = ImageUtils.getBitmap(p_w_picpathView.getContext(), filename); p_w_picpathView.setImageBitmap(bmp); }else{ //線程加載網絡圖片 p_w_picpathView.setImageBitmap(defaultBmp); queueJob(url, p_w_picpathView, width, height); } } } /** * 從緩存中獲取圖片 * @param url */ public Bitmap getBitmapFromCache(String url) { Bitmap bitmap = null; if (cache.containsKey(url)) { bitmap = cache.get(url).get(); } return bitmap; } /** * 從網絡中加載圖片 * @param url * @param p_w_picpathView * @param width * @param height */ public void queueJob(final String url, final ImageView p_w_picpathView, final int width, final int height) { final Handler handler = new Handler() { public void handleMessage(Message msg) { String tag = p_w_picpathViews.get(p_w_picpathView); if (tag != null && tag.equals(url)) { if (msg.obj != null) { p_w_picpathView.setImageBitmap((Bitmap) msg.obj); try { //向SD卡中寫入圖片緩存 ImageUtils.saveImage(p_w_picpathView.getContext(), FileUtils.getFileName(url), (Bitmap) msg.obj); } catch (IOException e) { e.printStackTrace(); } } } } }; pool.execute(new Runnable() { public void run() { Message message = Message.obtain(); message.obj = downloadBitmap(url, width, height); handler.sendMessage(message); } }); } /** * 下載圖片-可指定顯示圖片的高寬 * @param url * @param width * @param height */ private Bitmap downloadBitmap(String url, int width, int height) { Bitmap bitmap = null; try { //http加載圖片 bitmap = ApiClient.getNetBitmap(url); if(width > 0 && height > 0) { //指定顯示圖片的高寬 bitmap = Bitmap.createScaledBitmap(bitmap, width, height, true); } //放入緩存 cache.put(url, new SoftReference<Bitmap>(bitmap)); } catch (Exception e) { e.printStackTrace(); } return bitmap; } }
工具類使用
實例化時,可以設置默認的顯示圖片:
BitmapManager bmpManager = new BitmapManager(BitmapFactory.decodeResource(context.getResources(), R.drawable.loading));
調用加載圖片的方法:
bmpManager.loadBitmap(p_w_picpathURL, p_w_picpathView);
如果大家有什麼疑問的話,歡迎在下面回帖一起探討。
PS:
OSC Android客戶端下載地址: http://www.oschina.net/uploads/osc.apk
OSC iPhone客戶端下載地址: http://www.oschina.net/uploads/osc.ipa
OSC Windows Phone客戶端下載地址: http://www.oschina.net/uploads/osc.xap
轉載:http://www.oschina.net/question/157182_59454