Android-壓縮大圖到容量超小的圖片

Merbng發表於2018-05-16

原文

  • 壓縮圖片的寬高
    /**
     * 計算圖片的壓縮比
     *
     * @param options
     * @param reqWidth
     * @param reqHeight
     * @return
     */
    public static int calculateInSampleSize(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 halfHeight = height / 2;
            final int halfWidth = width / 2;
            while ((halfHeight / inSampleSize) >= reqHeight && 
                    (halfWidth / inSampleSize) >= reqWidth) {
                inSampleSize*=2;
            }
        }
        return inSampleSize;
    }
複製程式碼

呼叫calculateInSampleSize計算壓縮比。並解碼原圖為Bitmap:

    /**
     * @param imagePath
     * @param displayWidth
     * @param displayHeight
     * @return
     */
    private Bitmap compress(String imagePath, int displayWidth, int displayHeight) {
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;//只測量image 不載入到記憶體。
        BitmapFactory.decodeFile(imagePath, options);//測量image
        options.inPreferredConfig = Bitmap.Config.RGB_565;//設定565編碼格式,省記憶體,
        options.inSampleSize = calculateInSampleSize(options, displayWidth, displayHeight);
        options.inJustDecodeBounds = false;
        Bitmap bitmap = BitmapFactory.decodeFile(imagePath, options);//按照Options配置去載入圖片到記憶體
        return bitmap;
    }
複製程式碼

這裡比較重要的inJustDecodeBounds欄位,當inJustDecodeBounds為true時,呼叫BitmapFactory.decode時並沒有把圖片載入到記憶體中去,只是去測量圖片的寬高,不不佔用記憶體,當inSampleSize為false時,呼叫BitmapFactory.decoe時就把圖片載入到記憶體去了,所有獲取bitmap應在inJustDecodeBounds為false後的BitmapFactory.decode去獲取。

  • 設定編碼格式 android中預設格式是ARG8888,我們解碼圖片一般用ARG565節省圖片載入到記憶體的大小。
ALPHA_8 代表8位Alpha點陣圖 
ARGB_4444 代表16位ARGB點陣圖 
ARGB_8888 代表32位ARGB點陣圖 
RGB_565 代表8位RGB點陣圖 
複製程式碼

點陣圖位數越高代表其可以儲存的顏色資訊越多,當然影像也就越逼真

  • 圖片質量壓縮
ByteArrayOutputStream out = new ByteArrayOutputStream();//位元組流輸出
bitmap.compress(Bitmap.CompressFormat.JPEG,50,out);//壓縮成jpeg格式

複製程式碼

compress中第一個引數是輸出檔案的格式,在Bitmap列舉類CompressFormat中定義,有JPEG,PNG PNG,WEBP,一般選擇JPEG,壓縮出來的容量小,WEBP很耗時, 耗時時間比較:WEBP>PNG>JPEG, 壓縮大小:PNG>WEBP>JPEG. 第二個引數是壓縮的質量比例,也就是壓縮畫素的顯示色彩,當100時表示不壓縮。當為50時表示壓縮50%的質量。設定這個引數可以有效的極大的縮小圖片的大小,可以按照自己的需求進行設定, 但建議一般不要大於60.第三個引數就是想要寫入的圖片資料的位元組流陣列了。

  • 位元組流寫出檔案

我們經過上述步驟後,就拿到了位元組流資料了,此時我們可以根據專案需求直接上傳位元組流或者儲存為本地圖片再上傳。

  ByteArrayOutputStream out = new ByteArrayOutputStream();//位元組流輸出
        bitmap.compress(Bitmap.CompressFormat.JPEG, 50, out);//壓縮成jpeg格式  壓縮畫素質量為50%
        String fileName = imagePath.substring(imagePath.lastIndexOf("/") + 1, imagePath.lastIndexOf("."));//獲取檔名
        File outFile = new File("/storage/emulated/0/photoPickTemp", fileName + "_temp.jpeg");//建立壓縮後的image檔案
        try {
            if (!outFile.exists()) {//判斷檔案是否存在
                if (outFile.createNewFile()) {//判斷建立新檔案是否成功
                    FileOutputStream fos = new FileOutputStream(outFile);
                    byte[] bytes = out.toByteArray();//位元組陣列
                    int count = bytes.length;
                    fos.write(bytes, 0, count);
                    fos.close();//關閉流
                    out.close();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
複製程式碼
  • 獲取當前螢幕寬高

工具類

  • 封裝
public class ImageUtil {

    public ImageUtil(){

    }

    public static File compressImage(String imagePath,int displayWidth,int displayHeight){
        BitmapFactory.Options options=new BitmapFactory.Options();
        options.inJustDecodeBounds=true;//只測量image 不載入到記憶體
        BitmapFactory.decodeFile(imagePath,options);//測量image

        options.inPreferredConfig= Bitmap.Config.RGB_565;//設定565編碼格式 省記憶體
        options.inSampleSize=calculateInSampleSize(options,displayWidth,displayHeight);//獲取壓縮比 根據當前螢幕寬高去壓縮圖片

        options.inJustDecodeBounds=false;
        Bitmap bitmap=BitmapFactory.decodeFile(imagePath,options);//按照Options配置去載入圖片到記憶體

        ByteArrayOutputStream out=new ByteArrayOutputStream();//位元組流輸出
        bitmap.compress(Bitmap.CompressFormat.JPEG,50,out);//壓縮成JPEG格式 壓縮畫素質量為50%

        String fileName=imagePath.substring(imagePath.lastIndexOf("/")+1,imagePath.lastIndexOf("."));//獲取檔名稱
        File outFile=new File("/storage/emulated/0/PhotoPickTemp",fileName+"_temp.jpeg");//建立壓縮後的image檔案
        try {
            if(!outFile.exists()){//判斷新檔案是否存在
                if(outFile.createNewFile()){//判斷建立新檔案是否成功
                    FileOutputStream fos=new FileOutputStream(outFile);//建立一個檔案輸出流
                    byte[] bytes=out.toByteArray();//位元組陣列
                    int count=bytes.length;//位元組陣列的長度
                    fos.write(bytes,0,count);//寫到檔案中
                    fos.close();//關閉流
                    out.close();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return outFile;
    }

    public static int calculateInSampleSize(BitmapFactory.Options options,int reqWidth,int reqHeight){//計算圖片的壓縮比
        final int height=options.outHeight;//圖片的高度
        final int width=options.outWidth;//圖片的寬度 單位1px 即畫素點

        int inSampleSize=1;//壓縮比

        if(height>reqHeight||width>reqWidth){
            final int halfHeight=height/2;
            final int halfWidth=width/2;
            while ((halfHeight/inSampleSize)>=reqHeight
                    &&(halfWidth/inSampleSize)>=reqWidth){
                inSampleSize*=2;
            }
        }
        return inSampleSize;
    }
}
複製程式碼

相關文章