1. 程式人生 > >BitMap高效顯示策略(一):大圖的縮放和載入

BitMap高效顯示策略(一):大圖的縮放和載入

Android對不同螢幕和dpi的裝置有單個應用程式執行記憶體的限制。在有的裝置上,最嚴格的限制甚至可以小到只有16MB記憶體。

Bitmap會消耗很多記憶體,如果一個手機能夠拍攝2592x1936 pixels (5 MB)的圖片,bitmap的配置是使用ARGB_8888 (從Android 2.3開始的預設配置) ,那麼載入這張照片到記憶體會大概需要19MB(2592*1936*4 bytes) 的記憶體, 如果在只有16MB上限的裝置上執行的話,會迅速消耗掉裝置的整個記憶體。

BitmapFactory提供一系列載入BitMap的方法,這些方法在呼叫時會嘗試分配記憶體,如果載入一個圖片所需記憶體過大,就有可能拋OutOfMemory異常。

BitmapFactory.Options提供一些附加屬性來指定decode的選項,解析Bitmap時用到2個重要引數:

1.inJustDecodeBounds

設定為true後,decode方法解析Bitmap時會返回一個null,只講這個圖片的原始大小(單位是畫素)存入BitmapFactory.Options物件的options.outHeight和options.outWidth中,這樣可以在不分配記憶體的情況下得到圖片的尺寸資訊。

2.inSampleSize

這個引數代表縮小比例,如果是1,代表原始尺寸,如果>1,假設為2,則縮小後圖片畫素值為原圖的1/4(長1/2,寬1/2),同等格式下,佔用記憶體也變為原來的1/4。decoder以2的冪作為係數,接近2的冪的數值都會被處理為最接近的2的冪值,3.4~4,2.1~2,這樣。

縮小圖片會降低影象質量,所以載入圖片前,是否縮放,縮放比例等應綜合考慮以下因素:

1.載入完整圖片所需要耗費的記憶體

2.顯示這張圖片的元件的尺寸大小

3.螢幕大小與當前裝置的螢幕密度

如下方法計算出長寬都小於給定長寬時需要縮小的比例值。

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;
}
在呼叫以上方法前,記得設定options.inJustDecodeBounds = true;

呼叫後算出比例後,則呼叫

BitmapFactory.decodexxxx(res, resId, options);
解析。

示例程式碼:

public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
        int reqWidth, int reqHeight) {

    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeResource(res, resId, options);

    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

    options.inJustDecodeBounds = false;
    return BitmapFactory.decodeResource(res, resId, options);
}
mImageView.setImageBitmap(
    decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));

注意BitmapFactory.decodeStream解析網路流的時候情況比較特殊,參考這篇: