【Android - 進階】之圖片壓縮

        很多時候,如果APP需要下載和加載很多圖片(尤其是大圖片)的時候,就往往會報如下圖所示的錯誤:


        如上圖所示,OOM(OutOfMemoryError)表示內存溢出,這是因爲網絡或內存中的圖片被加載成Bitmap時耗費的內存超出了系統內存而造成內存溢出。解決這個問題有很多方法,這裏主要介紹其中的一種方法:圖片壓縮。


        這裏貼出一個工具類:

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;

/**
 * 圖片壓縮工具類
 */
public class ImageUtil {
    /**
     * 從指定路徑獲取Bitmap圖片
     *
     * @param imgPath 原始圖片的路徑
     */
    public static Bitmap getBitmap(String imgPath) {
        // Get bitmap through image path  
        BitmapFactory.Options newOpts = new BitmapFactory.Options();
        newOpts.inJustDecodeBounds = false;
        newOpts.inPurgeable = true;
        newOpts.inInputShareable = true;
        // Do not compress  
        newOpts.inSampleSize = 1;
        newOpts.inPreferredConfig = Config.RGB_565;
        return BitmapFactory.decodeFile(imgPath, newOpts);
    }

    /**
     * 將圖片存儲到指定路徑的文件中
     *
     * @param bitmap  原始圖片Bitmap
     * @param outPath 目標圖片路徑
     */
    public static void storeImage(Bitmap bitmap, String outPath) throws FileNotFoundException {
        FileOutputStream os = new FileOutputStream(outPath);
        bitmap.compress(Bitmap.CompressFormat.JPEG, 100, os);
    }

    /**
     * 尺寸壓縮(改變圖片的寬高),適合用於加載縮略圖
     *
     * @param imgPath 圖片路徑
     * @param pixelW  想要將圖片壓縮到的寬度
     * @param pixelH  想要將圖片壓縮到的高度
     */
    public Bitmap ratio(String imgPath, float pixelW, float pixelH) {
        BitmapFactory.Options newOpts = new BitmapFactory.Options();
        // 開始讀入圖片,此時把options.inJustDecodeBounds 設回true,即只讀邊不讀內容  
        newOpts.inJustDecodeBounds = true;
        newOpts.inPreferredConfig = Config.RGB_565;
        // Get bitmap info, but notice that bitmap is null now    
        Bitmap bitmap = BitmapFactory.decodeFile(imgPath, newOpts);

        newOpts.inJustDecodeBounds = false;
        int w = newOpts.outWidth;
        int h = newOpts.outHeight;
        // 想要縮放的目標尺寸  
        float hh = pixelH;// 設置高度爲240f時,可以明顯看到圖片縮小了  
        float ww = pixelW;// 設置寬度爲120f,可以明顯看到圖片縮小了  
        // 縮放比。由於是固定比例縮放,只用高或者寬其中一個數據進行計算即可    
        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(imgPath, newOpts);
        return bitmap;
    }

    /**
     * 尺寸壓縮(改變圖片的寬高),適合用於加載縮略圖
     *
     * @param image  圖片的Bitmap
     * @param pixelW 想要將圖片壓縮到的寬度
     * @param pixelH 想要將圖片壓縮到的高度
     */
    public Bitmap ratio(Bitmap image, float pixelW, float pixelH) {
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        image.compress(Bitmap.CompressFormat.JPEG, 100, os);
        if (os.toByteArray().length / 1024 > 1024) {//判斷如果圖片大於1M,進行壓縮避免在生成圖片(BitmapFactory.decodeStream)時溢出
            os.reset();//重置baos即清空baos    
            image.compress(Bitmap.CompressFormat.JPEG, 50, os);//這裏壓縮50%,把壓縮後的數據存放到baos中    
        }
        ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray());
        BitmapFactory.Options newOpts = new BitmapFactory.Options();
        //開始讀入圖片,此時把options.inJustDecodeBounds 設回true了    
        newOpts.inJustDecodeBounds = true;
        newOpts.inPreferredConfig = Config.RGB_565;
        Bitmap bitmap = BitmapFactory.decodeStream(is, null, newOpts);
        newOpts.inJustDecodeBounds = false;
        int w = newOpts.outWidth;
        int h = newOpts.outHeight;
        float hh = pixelH;// 設置高度爲240f時,可以明顯看到圖片縮小了  
        float ww = pixelW;// 設置寬度爲120f,可以明顯看到圖片縮小了  
        //縮放比。由於是固定比例縮放,只用高或者寬其中一個數據進行計算即可    
        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了    
        is = new ByteArrayInputStream(os.toByteArray());
        bitmap = BitmapFactory.decodeStream(is, null, newOpts);
        return bitmap;
    }

    /**
     * 質量壓縮,然後存儲到指定路徑
     *
     * @param image   原始圖片Bitmap
     * @param outPath 目標圖片路徑
     * @param maxSize 圖片將被壓縮成這麼多KB
     */
    public void compressAndGenImage(Bitmap image, String outPath, int maxSize) throws IOException {
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        // scale  
        int options = 100;
        // Store the bitmap into output stream(no compress)  
        image.compress(Bitmap.CompressFormat.JPEG, options, os);
        // Compress by loop  
        while (os.toByteArray().length / 1024 > maxSize) {
            // Clean up os  
            os.reset();
            // interval 10  
            options -= 10;
            image.compress(Bitmap.CompressFormat.JPEG, options, os);
        }
        // Generate compressed image file  
        FileOutputStream fos = new FileOutputStream(outPath);
        fos.write(os.toByteArray());
        fos.flush();
        fos.close();
    }

    /**
     * 質量壓縮並存儲到指定路徑
     *
     * @param imgPath     想要壓縮的圖片路徑
     * @param outPath     目標圖片路徑
     * @param maxSize     圖片將被壓縮成這麼多KB
     * @param needsDelete 壓縮後是否刪除原始圖片
     */
    public void compressAndGenImage(String imgPath, String outPath, int maxSize, boolean needsDelete) throws IOException {
        compressAndGenImage(getBitmap(imgPath), outPath, maxSize);
        if (needsDelete) {
            File file = new File(imgPath);
            if (file.exists()) {
                file.delete();
            }
        }
    }

    /**
     * 尺寸壓縮後保存圖片
     *
     * @param image   原始圖片Bitmap
     * @param outPath 目標圖片路徑
     * @param pixelW  想要將圖片壓縮到的寬度
     * @param pixelH  想要將圖片壓縮到的高度
     */
    public void ratioAndGenThumb(Bitmap image, String outPath, float pixelW, float pixelH) throws FileNotFoundException {
        Bitmap bitmap = ratio(image, pixelW, pixelH);
        storeImage(bitmap, outPath);
    }

    /**
     * 尺寸壓縮後保存圖片,並決定是否刪除原始圖片
     *
     * @param imgPath     想要壓縮的圖片路徑
     * @param outPath     目標圖片路徑
     * @param pixelW      想要將圖片壓縮到的寬度
     * @param pixelH      想要將圖片壓縮到的高度
     * @param needsDelete 是否刪除原始圖片
     */
    public void ratioAndGenThumb(String imgPath, String outPath, float pixelW, float pixelH, boolean needsDelete) throws FileNotFoundException {
        Bitmap bitmap = ratio(imgPath, pixelW, pixelH);
        storeImage(bitmap, outPath);
        if (needsDelete) {
            File file = new File(imgPath);
            if (file.exists()) {
                file.delete();
            }
        }
    }
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章