歡迎各位加入個人Android開發羣[257053751]java
KJFrameForAndroid框架項目地址:https://github.com/kymjs/KJFrameForAndroidgit
咱們在寫Android程序的時候,確定會用到不少圖片。那麼對於圖片的壓縮處理天然是必不可少。爲何要壓縮?我想這個問題沒必要在強調了,每一個人在最初學習Android的時候確定都會知道這麼一個緣由:咱們編寫的應用程序都是有一個最大內存限制,其中JAVA程序和C程序(NDK調用時)共享這一塊內存大小,程序佔用了太高的內存就容易出現OOM(OutOfMemory)異常。至於這個最大內存是多少,咱們能夠經過調用Runtime.getRuntime().maxMemory()方法驗證一下。
正由於受到內存大小限制這一關鍵緣由(其實不止這個緣由,我想一張1M的圖片和一張10k的圖片,載入的速度必然也是不一樣的吧)。 若是你的控件大小隻有40*40像素的大小,只是爲了顯示一張縮略圖,這時候把一張1024*768像素的圖片徹底加載到內存中顯然是不值得的,所以咱們都會對圖片作壓縮處理。
BitmapFactory這個類提供了多個方法(decodeByteArray, decodeFile, decodeResource等)用於建立Bitmap對象,咱們能夠根據圖片的來源選擇合適的方法。然而這些方法會爲已經讀取的bitmap分配內存,這時若是是一張很是大的圖片就會致使OOM出現。爲此,每一種解析方法都提供了一個BitmapFactory.Options參數,能夠經過將這個參數的inJustDecodeBounds屬性設置爲true就可讓解析方法禁止爲bitmap分配內存,可是如此設置後BitmapFactory的返回值也再也不是一個Bitmap對象,而是null。雖然Bitmap是null了,可是BitmapFactory.Options的outWidth、outHeight和outMimeType屬性都會被賦值。使用這個技巧讓咱們能夠在加載圖片以前就獲取到圖片的長寬值和類型,從而根據狀況對圖片進行壓縮。
github
BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeFile(pathName, options); int h = options.outHeight; int w = options.outWidth; String type = options.outMimeType;
那麼知道了圖片的寬高,要如何壓縮呢?BitmapFactory.Options有一個inSampleSize屬性,這個int值表示圖片的原寬高變爲1/inSampleSize倍,若是原圖是1024*768,inSampleSize=2,那麼壓縮後圖片就變成了512*384。
最後將BitmapFactory.Options設置合適的inSampleSize值,而且記得將inJustDecodeBounds設置回false,再調用一次BitmapFactory相應的建立Bitmap的方法,並把Options傳入,就能夠獲得壓縮後的圖片了。數組
這裏有一個節選自開源Android應用開發框架KJFrameForAndroid中的一段代碼框架
/** * 圖片壓縮處理(使用Options的方法) * * @使用方法 首先你要將Options的inJustDecodeBounds屬性設置爲true,BitmapFactory.decode一次圖片。 * 而後將Options連同指望的寬度和高度一塊兒傳遞到到本方法中。 * 以後再使用本方法的返回值作參數調用BitmapFactory.decode建立圖片。 * * @explain BitmapFactory建立bitmap會嘗試爲已經構建的bitmap分配內存 * ,這時就會很容易致使OOM出現。爲此每一種建立方法都提供了一個可選的Options參數 * ,將這個參數的inJustDecodeBounds屬性設置爲true就可讓解析方法禁止爲bitmap分配內存 * ,返回值也再也不是一個Bitmap對象, 而是null。雖然Bitmap是null了,可是Options的outWidth、 * outHeight和outMimeType屬性都會被賦值。 * @param reqWidth * 目標寬度 * @param reqHeight * 目標高度 */ public static BitmapFactory.Options calculateInSampleSize( final BitmapFactory.Options options, int reqWidth, int reqHeight) { // 源圖片的高度和寬度 final int height = options.outHeight; final int width = options.outWidth; int inSampleSize = 1; if (height > reqHeight || width > reqWidth) { // 計算出實際寬高和目標寬高的比率 final int heightRatio = Math.round((float) height / (float) reqHeight); final int widthRatio = Math.round((float) width / (float) reqWidth); // 選擇寬和高中最小的比率做爲inSampleSize的值,這樣能夠保證最終圖片的寬和高 // 必定都會大於等於目標的寬和高。 inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio; } // 設置壓縮比例 options.inSampleSize = inSampleSize; options.inJustDecodeBounds = false; return options; }
以上的方法適合使用在讀取一個未知來源的圖片時使用,由於你不知道這個未知來源圖片的大小,那麼還有一種方法是用在已經載入內存的圖片,對已經載入內存的圖片作壓縮之後從新保存到本地,從而能夠把一張本來1M大小的圖片變成一張10K的圖片。
這種方法的核心思想是首先將圖片轉成一個輸出流,並記錄輸出流的byte數組大小,經過調用bitmap對象的compress方法,對圖片作一次壓縮以及格式化,並將byte數組大小與指望壓縮的目標大小比對,得出壓縮比率,並調用Bitmap的縮放方法,縮放計算出的壓縮比率,從而獲得壓縮後的方法。
下面咱們繼續來看KJFrameForAndroid框架中的另外一段代碼:post
/** * 圖片壓縮方法:(使用compress的方法) * * @explain 若是bitmap自己的大小小於maxSize,則不做處理 * @param bitmap * 要壓縮的圖片 * @param maxSize * 壓縮後的大小,單位kb */ public static void imageZoom(Bitmap bitmap, double maxSize) { // 將bitmap放至數組中,意在得到bitmap的大小(與實際讀取的原文件要大) ByteArrayOutputStream baos = new ByteArrayOutputStream(); // 格式、質量、輸出流 bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos); byte[] b = baos.toByteArray(); // 將字節換成KB double mid = b.length / 1024; // 獲取bitmap大小 是容許最大大小的多少倍 double i = mid / maxSize; // 判斷bitmap佔用空間是否大於容許最大空間 若是大於則壓縮 小於則不壓縮 if (i > 1) { // 縮放圖片 此處用到平方根 將寬帶和高度壓縮掉對應的平方根倍 // (保持寬高不變,縮放後也達到了最大佔用空間的大小) bitmap = scale(bitmap, bitmap.getWidth() / Math.sqrt(i), bitmap.getHeight() / Math.sqrt(i)); } } /*** * 圖片的縮放方法 * * @param src * :源圖片資源 * @param newWidth * :縮放後寬度 * @param newHeight * :縮放後高度 */ public static Bitmap scale(Bitmap src, double newWidth, double newHeight) { // 記錄src的寬高 float width = src.getWidth(); float height = src.getHeight(); // 建立一個matrix容器 Matrix matrix = new Matrix(); // 計算縮放比例 float scaleWidth = ((float) newWidth) / width; float scaleHeight = ((float) newHeight) / height; // 開始縮放 matrix.postScale(scaleWidth, scaleHeight); // 建立縮放後的圖片 return Bitmap.createBitmap(src, 0, 0, (int) width, (int) height, matrix, true); }