Android項目實戰-ListView異步圖片加載及壓縮緩存
這回先講一下ListView異步加載圖片的問題,相關的文章很多,不過在這里我將加載、壓縮下載、存儲到SD卡等功能全部放上來,方便大家使用或者研究。
1. ListView中的SimpleAdapter中的操作
大家在使用ListView顯示復雜的頁面時,我們都全重寫一個SimpleAdapter,重寫其中的getView方法來顯示具體內容。相關的內容我們下篇文章再說,這里主要看下圖片顯示的代碼。
//圖片內容顯示 if(!topic.getImage_value().equals("")){ vh.tl_ll_content_image.setVisibility(View.VISIBLE); //加上標識,要不圖片加載會變亂 vh.tl_img_content_image.setTag(topic.getTid()); vh.tl_img_content_image.setImageResource(R.drawable.default_image_load); //新開異步加載圖片 LoadingTopicImageAsyncTaskloadingTopicImageAsyncTask = new LoadingTopicImageAsyncTask(vh.tl_img_content_image, topic,ctxContext,true,String.valueOf(topic.getTid())); loadingTopicImageAsyncTask.execute(); }
·vh. tl_ll_content_image是我們在ListView中要顯示相關圖片的控件。Topic是我們通過getView取到的當前行的數據對象。Topic.getImage_value()中則是要顯示的圖片url地址,比如:http://www.huoban168.com/huoban/upload/topic/2012/02/weibo_1329196372_1112447.png。
·為圖片控件加上setTag主要是防止圖片混亂。
·LoadingTopicImageAsyncTask是我們新開一個異步,下載或者加載相關的圖片的類。基本上主要的操作都在此類中進行。
2. LoadingTopicImageAsyncTask類中的操作
packagehb.hbwb.asynctask; importhb.hbwb.model.beans.Topic; importhb.hbwb.tools.PicTool; importhb.hbwb.var.PublicVariable; importandroid.app.Activity; importandroid.app.Dialog; importandroid.graphics.Bitmap; importandroid.os.AsyncTask; importandroid.view.View; importandroid.view.View.OnClickListener; importandroid.widget.ImageView; /** * @name 異步加載圖片 * @author zhang.yue * @create_date 2011-12-15 * @last_edit_author * @last_edit_date * @remark * @edit_remark */ public classLoadingTopicImageAsyncTask extends AsyncTask<String, Integer,String> { private ImageView imageView = null; private Topic topic = null; private Bitmap bt = null; private Activity activity; private boolean isClick = true; private String tag = ""; /** * 重寫構造 * * @paramheaderView * 圖像控件 * @paramtopic * @paramactivity * @paramisClick * 是否可以點擊放大 */ public LoadingTopicImageAsyncTask(ImageViewimageView, Topic topic, Activity activity, booleanisClick, String tag) { super(); this.imageView = imageView; this.topic = topic; this.activity = activity; this.isClick = isClick; this.tag = tag; } @Override protected String doInBackground(String...params) { // 添加如果是本地緩存的從本地讀取... bt =PublicVariable.allTopicImage.get(String.valueOf(topic.getTid())); if (bt == null) { // 圖像獲取并保存 if (topic.getImage_value().equals("")){ bt = null; } else { bt =PicTool.ReturnBitMap(topic.getImage_value()); } } if (bt!=null) { if(bt.getWidth()>PublicVariable.TOPIC_IMAGE_SHOW-40 ||bt.getHeight()>PublicVariable.TOPIC_IMAGE_SHOW_HEIGHT-40) { bt =PicTool.ChangeSizeBitMap(bt, PublicVariable.TOPIC_IMAGE_SHOW-40); } } return null; } @Override protected void onPostExecute(String result) { super.onPostExecute(result); if (imageView != null &&topic != null && !tag.equals("")) { if(imageView.getTag().toString().equals(tag)) { if (bt != null) { if(isClick) { //點擊大圖效果 imageView.setOnClickListener(newOnClickListener() { @Override publicvoid onClick(View v) { ShowImageClickListener(bt,activity); } }); } imageView.setImageBitmap(bt); PublicVariable.allTopicImage.put( String.valueOf(topic.getTid()),bt); // 保存到數組中 } } } } /** * 圖片點擊功能事件處理 * * @parambt * @paramactivity */ public static voidShowImageClickListener(Bitmap bt, Activity activity) { Bitmap maxBt =PicTool.ChangeSizeBitMap(bt, PublicVariable.TOPIC_IMAGE_SHOW); ImageView showImageView = newImageView(activity); showImageView.setImageBitmap(maxBt); //兩個嵌套的TabHost中,必須使用activity.getParent() //在首頁中使用并無報錯,全局使用 Dialog d = newDialog(activity.getParent(), hb.hbwb.R.style.no_back_title_dialog); d.setContentView(showImageView); d.setCanceledOnTouchOutside(true); d.show(); } }
·PublicVariable.allTopicImage是我們寫的一個HashMap,存儲Bitmap的一個集合,如果在程序的運行過程中多次顯示同一個圖片即可直接從這里面取出Bitmap并顯示,可以節省網上加載或者sd卡讀取的步驟,鍵值是topic的tid,一個微博僅顯示一張圖片。
·如果上述的Bitmap集合中不存在圖片的話,調用PicTool類中的ReturnBitMap返回SD卡上緩存的圖片。
·如果圖片超過我們規定的一個大小,那么調用PicTool類中的ChangeSizeBitMap方法。
·onPostExecute方法是線程執行完成后,我們將Bitmap對象給予Image圖片控件并顯示出來。在這里我們還添加了點擊事件。
3. ReturnBitMap解析
/** * 返回圖片文件用 * * @paramurl * 圖片url地址 *@return */ public static Bitmap ReturnBitMap(String url) { String imgName =StringTool.GetImageNameForUrl(url); if (imgName.equals("")) { return null; } else { FileTool ft = newFileTool(); String path =FileFinals.SDCARDROOT + File.separator + FileFinals.SDCARDIMAGEPATH; // 圖片文件存在并且不是刷新的時候 if (ft.IsFileExist(imgName +FileFinals.IMAGE_SYSTEM_EXT, FileFinals.SDCARDIMAGEPATH)) { Bitmap bm =ReturnLocalBitMap(path + File.separator + imgName); if(bm==null){ returnReturnWebBitMap(url, imgName, path); }else{ returnbm; } } else { // 先刪除原有的 returnReturnWebBitMap(url, imgName, path); } } } /** * 本地獲取圖片信息 * * @parampath * 圖片路徑 *@return bitmap對象 */ public static Bitmap ReturnLocalBitMap(Stringpath) { Bitmap bitmap =BitmapFactory.decodeFile(path + ".image"); return bitmap; } /** * 抓取遠程圖片 * * @paramurl * 圖片地址 * @paramimgName * 圖片名 * @parampath * 地址 *@return */ public static Bitmap ReturnWebBitMap(Stringurl, String imgName, String path) { Bitmap bitmap = null; try { // 圖片大小判斷操作 InputStream size_is =HTMLTool.GetHttpConnection(url).getInputStream(); BitmapFactory.Options op =new BitmapFactory.Options(); op.inJustDecodeBounds =true; @SuppressWarnings("unused") Bitmap size_bitmap =BitmapFactory.decodeStream(size_is, null, op); op.inJustDecodeBounds =true; // 僅獲取寬高信息,不加載整個圖片 boolean isop = false; int size =PublicVariable.TOPIC_IMAGE_SHOW; // 設定獲取的大小不能超過寬高 int bili = 0; // 壓縮比例 BitmapFactory.Options op_new= new BitmapFactory.Options(); if(op.outWidth>op.outHeight){ if (op.outWidth> size) { bili =op.outWidth / size; isop =true; } }else{ if (op.outHeight> size) { bili =op.outHeight / size; isop =true; } } // 根據比例判斷 if (bili != 0) { if (bili < 2) { bili = 2; }else{ bili = bili*2-2; } if(bili%2!=0){ bili =bili+1; } op_new.inSampleSize= bili; } else { isop = false; } System.out.println(bili); size_bitmap = null; size_is.close(); // 如果進行過壓縮,加載處理后的圖片,如果沒有直接加載 InputStream is =HTMLTool.GetHttpConnection(url).getInputStream(); if (isop) { op_new.inPreferredConfig= Bitmap.Config.ARGB_4444; op_new.inPurgeable= true; op_new.inInputShareable= true; bitmap =BitmapFactory.decodeStream(is, null, op_new); } else { bitmap =BitmapFactory.decodeStream(is); } // 將圖片寫入到內存卡中,做為緩存,將來直接本地讀取 WriteBitmapToSdCard(FileFinals.SDCARDIMAGEPATH,path + File.separator + imgName, bitmap); is.close(); } catch (Exception e) { return null; } return bitmap; }
·StringTool.GetImageNameForUrl是截取文件名,比如:http://www.huoban168.com/huoban/upload/topic/2012/02/weibo_1329196372_1112447.png,返回的是weibo_1329196372_1112447.png這一部分的內容。
·接下來查看當前SD卡的緩存文件路徑下有沒有這張圖,如果有的話,直接調用ReturnLocalBitMap加載圖片,如果沒有的話,調用ReturnWebBitMap方法去調用網絡圖片并下載下來。
·后綴名都加了一個".image",目的是防止Android的圖片瀏覽器去加載這些圖片。
·ReturnWebBitMap中,先通過HTMLConnection獲取圖片的InputStream二進制流,然后通過 op.inJustDecodeBounds = true; // 這個方法,獲取一個只有高度等屬性的Bitmap對象,接著根據圖片原始大小獲取需要大小的壓縮比例op_new.inSampleSize = bili;。
·最后取到壓縮過的Bitmap對象寫入到內存卡或者直接顯示均可。至此,圖片的下載和保存已完成。
4. ChangeSizeBitMap解析
/** * 等比例縮放圖片 * * @param bt * bitmap圖片對象 * @param resize * 狀態,是否進行縮放 * @return bitmap圖片對象 */ publicstatic Bitmap ChangeSizeBitMap(Bitmap bt, int resize) { intsrc_width = bt.getWidth(); intsrc_height = bt.getHeight(); floatwidth = (float)bt.getWidth(); floatheight = (float)bt.getHeight(); floatbmpWidth = (float) resize / width; floatbmpHeight = (float) resize / height; if(width >= height) { if(width > resize) { bmpWidth= (float) resize / height; bmpHeight= bmpWidth; } }else{ if(height > resize) { bmpHeight= (float) resize / height; bmpWidth= bmpHeight; } } if(bmpWidth > 1 || bmpHeight > 1) { returnbt; } Matrixmatrix = new Matrix(); matrix.postScale(bmpWidth,bmpHeight); BitmapresizeBmp = Bitmap.createBitmap(bt, 0, 0, src_width, src_height, matrix, true); returnresizeBmp; }這個方法里的東西就很簡單了,不過計算的方式可以參考下,和下載圖片時候的差不多。
轉自:http://blog.csdn.net/zhangyue0503/article/details/7258347