android‎ > ‎

android BitmapFactory 取得縮圖

出處: http://www.allenj.net/archives/2763

最近在開發 Android 的載入照片功能
遇到一個錯誤:OutOfMemoryError
直覺告訴我這個問題可能是照片檔案太大
行動裝置的記憶體通常都不大
每個應用程式又被分配使用量
一不小心就會使圖檔超出限制

如何縮圖?

產生縮圖要利用 BitmapFactory 類別來處理
這裡要注意的是如果一開始就將圖檔載入
等於是直接放在記憶體
還是會發生 OutOfMemoryError
在 BitmapFactory.Options 有一個 inJustDecodeBounds
是設定只取得圖檔的寬高
不會真正載入到記憶體
有原始寬高跟縮圖寬高去算出取樣數
再用 BitmapFactory 設定 inSampleSize 將圖檔載入變成縮圖

程式步驟

  1. 取得原始圖檔寬高
  2. 動態計算出取樣數
  3. 載入記憶體產生縮圖

分二次載入圖檔

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
private Bitmap getBitmap(Uri uri) {
    try {
        InputStream in = getContentResolver().openInputStream(uri);
 
        //第一次 decode,只取得圖片長寬,還未載入記憶體
        BitmapFactory.Options opts = new BitmapFactory.Options();
        opts.inJustDecodeBounds = true;
        BitmapFactory.decodeStream(in, null, opts);
        in.close();
 
        //取得動態計算縮圖長寬的 SampleSize (2的平方最佳)
        int sampleSize = computeSampleSize(opts, -1, 64 * 64);
 
        //第二次 decode,指定取樣數後,產生縮圖
        in = getContentResolver().openInputStream(uri);
        opts = new BitmapFactory.Options();
        opts.inSampleSize = sampleSize;
 
        Bitmap bmp = BitmapFactory.decodeStream(in, null, opts);
        in.close();
 
        return bmp;
    } catch (Exception err) {
        return null;
    }
}

動態計算取樣數

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public static int computeSampleSize(BitmapFactory.Options options, int minSideLength, int maxNumOfPixels) {
 
    int initialSize = computeInitialSampleSize(options, minSideLength,maxNumOfPixels);
    int roundedSize;
 
    if (initialSize         roundedSize = 1;
        while (roundedSize < initialSize) {
            roundedSize <        }
    } else {
        roundedSize = (initialSize + 7) / 8 * 8;
    }
 
    return roundedSize;
}
 
private static int computeInitialSampleSize(BitmapFactory.Options options,int minSideLength, int maxNumOfPixels) {
 
    double w = options.outWidth;
    double h = options.outHeight;
 
    int lowerBound = (maxNumOfPixels == -1) ? 1 :
            (int) Math.ceil(Math.sqrt(w * h / maxNumOfPixels));
    int upperBound = (minSideLength == -1) ? 128 :
            (int) Math.min(Math.floor(w / minSideLength),
            Math.floor(h / minSideLength));
 
    if (upperBound < lowerBound) {
        // return the larger one when there is no overlapping zone.
        return lowerBound;
    }
 
    if ((maxNumOfPixels == -1) &&
            (minSideLength == -1)) {
        return 1;
    } else if (minSideLength == -1) {
        return lowerBound;
    } else {
        return upperBound;
    }
}

最後 ImageView 設定 Bitmap

1
2
ImageView imageView = (ImageView)findViewById(R.id.imageView1);
imageView.setImageBitmap(getBitmap(uri));
Comments