編輯:關於Android編程
在上一篇專題Android開發之圖片處理專題(一):利用軟引用構建圖片高速緩存中我們講述了如何利用軟引用技術構建高速緩存。那麼想要用到圖片,首先得有圖片的來源。一般而言,一個應用的圖片資源都是從服務器處獲得的。今天,我們利用Android開發之網絡請求通信專題(二):基於HttpClient的文件上傳下載裡面封裝好的httpUtils來實現圖片的下載,然後加載到本地配合軟引用緩存使用,以一個listView為例子來說明。
public interface FreedomCallback { /** * @Title: imageLoaded * @Description: TODO * @param imageDrawable 傳回的bitmap對象 * @param tag 用於listView查找控件 * @throws */ public void imageLoaded(Bitmap imageDrawable, Object tag); }
public class ImageDownload { private static final String TAG = "ImageDownload"; private static String path; //構建緩存地址 static { if (Environment.getExternalStorageState().equals( Environment.MEDIA_MOUNTED)) { File externalStorageDirectory = Environment .getExternalStorageDirectory(); path = externalStorageDirectory.getAbsolutePath() + ConstantValue.IMAGE_PATH; File directory = new File(path); if (!directory.exists()) { directory.mkdirs(); } } else { path = null; Log.e(TAG, "no sdcard."); } } /** * @Title: loadDrawable * @Description: 下載圖片 * @param imageUrl * @param simpleName * @param imageCallback * @throws */ public void loadDrawable(String imageUrl, String simpleName, FreedomCallback imageCallback) { new FreedomLoadTask(imageCallback).execute(imageUrl, simpleName); } /** * @Title: loadImageFromUrl * @Description: 根據地址下載圖片 * @param url * @return * @throws */ public Bitmap loadImageFromUrl(String url) { InputStream i = null; try { FreedomHttpClientUtil util = new FreedomHttpClientUtil(); util.open(url, FreedomHttpClientUtil.GET); util.post(); i = util.openInputStream(); //拿到流後創建bitmap Bitmap bitmap = BitmapFactory.decodeStream(i); return bitmap; } catch (Exception e) { e.printStackTrace(); } return null; } /** * @Title: saveImage2SDCard * @Description: 將圖片存放到SD卡中 * @param simpleName * @param bitmap * @throws */ public void saveImage2SDCard(String simpleName, Bitmap bitmap) { if (StringUtils.isNotBlank(path)) { File file = new File(new File(path), simpleName + ".png"); FileOutputStream fOut = null; try { fOut = new FileOutputStream(file); //將bitmap寫入流中 bitmap.compress(Bitmap.CompressFormat.PNG, 100, fOut); fOut.flush(); } catch (Exception e) { org.cao.optimization.util.Log.info("文件無法創建"); e.printStackTrace(); } finally { if (fOut != null) try { fOut.close(); } catch (IOException e) { e.printStackTrace(); } } } } /** * @ClassName: FreedomLoadTask * @author victor_freedom ([email protected]) * @createddate 2015-1-31 下午11:39:18 * @Description: 圖片下載,返回一個bitmap對象 */ class FreedomLoadTask extends AsyncTask{ private FreedomCallback imageCallback; public FreedomLoadTask(FreedomCallback imageCallback) { this.imageCallback = imageCallback; } @Override protected Bitmap doInBackground(String... params) { // 傳入兩個 參數,一個是下載路徑,一個是文件名稱 Bitmap bitmap = loadImageFromUrl(params[0]); if (bitmap != null) { // 保存到SD卡中 saveImage2SDCard(params[1], bitmap); // 加入緩存中 ImageCache.getInstance().cacheImage( new Image(params[1], bitmap)); } return bitmap; } @Override protected void onPostExecute(Bitmap result) { // 下載成功後執行回調接口 imageCallback.imageLoaded(result, null); super.onPostExecute(result); } } }
//拿到圖片的名字 final String id = infos.getName(); //給此處ImageView空間設置id continer.image.setTag(id); //根據名字首先從軟應用中拿到bitmap Image image = ImageCache.getInstance().getImage(id); Bitmap bitmap = image.getBitmap(); //判斷bitmap是否為空 if (bitmap == null) { // 判斷本地SDCard中是否有相關文件 if (Image.hasFile(id)) { InputStream is; try { is = new FileInputStream(Image.getFileFromSDCard(id)); bitmap = BitmapFactory.decodeStream(is); ImageCache.getInstance().cacheImage(new Image(id, bitmap)); continer.image.setImageBitmap(bitmap); } catch (FileNotFoundException e) { e.printStackTrace(); } } else { //本地也沒有,就從服務器下載 ImageDownload download = new ImageDownload(); download.loadDrawable(news.getImgUrl(), id + "", new FreedomCallback() { @Override public void imageLoaded(Bitmap imageDrawable, Object tag) { if (imageDrawable != null) { //構建adapter時傳入的回調接口,如果下載圖片成功,回調此方法 listCallback.imageLoaded(imageDrawable, id); } } }); continer.image.setImageResource(R.drawable.ic_launcher); } } else { //如果軟引用還存在,則直接拿來用 continer.image.setImageBitmap(bitmap); }
adapter = new NewsAdapter(context, new ListCallback() { @Override public void imageLoaded(Bitmap bitmap, Object tag) { //根據回調傳回的tag找到對應的ImageView ImageView imageView = (ImageView) listView.findViewWithTag(tag); if (imageView != null) { imageView.setImageBitmap(bitmap); } } }); listView = new ListView(context); listView.setAdapter(adapter);
//圖片質量壓縮 public static Bitmap compressImage(Bitmap image) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); image.compress(Bitmap.CompressFormat.JPEG, 100, baos);// 質量壓縮方法,這裡100表示不壓縮,把壓縮後的數據存放到baos中 int options = 100; while (baos.toByteArray().length / 1024 > 100) { // 循環判斷如果壓縮後圖片是否大於100kb,大於繼續壓縮 baos.reset();// 重置baos即清空baos image.compress(Bitmap.CompressFormat.JPEG, options, baos);// 這裡壓縮options%,把壓縮後的數據存放到baos中 options -= 10;// 每次都減少10 } ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());// 把壓縮後的數據baos存放到ByteArrayInputStream中 Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null);// 把ByteArrayInputStream數據生成圖片 return bitmap; } //圖片寬高壓縮 public static Bitmap getimage(String srcPath) { BitmapFactory.Options newOpts = new BitmapFactory.Options(); // 開始讀入圖片,此時把options.inJustDecodeBounds 設回true了 newOpts.inJustDecodeBounds = true; Bitmap bitmap = BitmapFactory.decodeFile(srcPath, newOpts);// 此時返回bm為空 newOpts.inJustDecodeBounds = false; int w = newOpts.outWidth; int h = newOpts.outHeight; // 這個設置圖片的寬高,根據實際需要設定 float hh = 800f;// 這裡設置高度為800f float ww = 480f;// 這裡設置寬度為480f // 縮放比。由於是固定比例縮放,只用高或者寬其中一個數據進行計算即可 int be = 1;// be=1表示不縮放 if (w > h && w > ww) {// 如果寬度大的話根據寬度固定大小縮放 be = (int) (newOpts.outWidth / ww); } else if (w < h && h > hh) {// 如果高度高的話根據寬度固定大小縮放 be = (int) (newOpts.outHeight / hh); } if (be <= 0) be = 1; newOpts.inSampleSize = be;// 設置縮放比例 // 重新讀入圖片,注意此時已經把options.inJustDecodeBounds 設回false了 bitmap = BitmapFactory.decodeFile(srcPath, newOpts); return compressImage(bitmap);// 壓縮好比例大小後再進行質量壓縮 }
有時候,我們還可以讓系統自動的給我們壓縮,只要確保不會出現OOM錯誤又能讓圖片以最大性能去展示
File file = Image.getFileFromSDCard(name, "pure"); Options opts = new Options(); opts.inJustDecodeBounds = true; BitmapFactory.decodeFile(file.getAbsolutePath(), opts); opts.inJustDecodeBounds = false; // 根據設定的值讓系統自動壓縮,這行代碼的參數是第一個是BitmapFactory.Options,第二個參數是調整後圖片最小的寬或者高,第三個參數是調整後圖片的內存占用量上限。 opts.inSampleSize = Util.computeSampleSize(opts, 600,(int) (1 * 1024 * 1024)); opts.inDither = false; opts.inPreferredConfig = Bitmap.Config.ARGB_8888; bitmap = BitmapFactory.decodeFile(file.getAbsolutePath(), opts);
圖片壓縮的步驟一般設定在圖片下載完之後。
至此,圖片從下載到顯示的所有流程,我們已經實現。當然,這只是一種實現方式,圖片的異步加載還可以有其他的實現方式。今天我們回顧了Android開發之網絡請求通信專題(二):基於HttpClient的文件上傳下載的內容裡封裝的httputils
Profile GPU RenderingAndroid開發者選項中提供了Profile GPU Rendering功能,用於在屏幕上實時顯示GPU渲染每一幀圖像花費的時
OKHttp是一款高效的HTTP客戶端,支持連接同一地址的鏈接共享同一個socket,通過連接池來減小響應延遲,還有透明的GZIP壓縮,請求緩存等優勢。(GitHub頁:
簡介DancingNumberView是一個用於跳動顯示文本中數字的控件,繼承自TextView.這種控件一般用於顯示金額等對用戶較為敏感的數字,讓UI交互更加生動.它具
Android會為每個apk進程分配一個單獨的空間(比如只能訪問/data/data/自己包名下面的文件),一般情況下apk之間是禁止相互訪問數據的。通過Shared U