1. 程式人生 > >android圖片:高效載入大圖

android圖片:高效載入大圖

圖片有各種大小和形狀,很多時候我們要顯示的圖片的解析度大小遠大於手機螢幕的解析度,這時候我們通常是先對圖片做壓縮再載入到記憶體顯示,因為一方面原圖佔用太多記憶體,容易導致OOM,另一方面,只要壓縮適當,壓縮後的圖片在手機上的顯示效果和原圖沒太大差別,手機解析度就這麼多,你原圖解析度再高,顯示在手機螢幕上,也超不過其最大解析度,視覺上看起來效果一樣,佔用的記憶體卻大大不一樣。

BitmapFactory提供了對圖片操作的一系列方法,下面是對圖片壓縮的一個寫法。

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

    // First decode with inJustDecodeBounds=true to check dimensions
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeResource(res, resId, options);

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

    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false;
    return BitmapFactory.decodeResource(res, resId, options);
}

開始時將inJustDecodeBounds屬性設定為true,避免為圖片分配記憶體,但是卻可以讀取圖片的大小,再通過這個大小計算出壓縮倍數inSampleSize,之後再將inJustDecodeBounds屬性設定為false,讀取壓縮後的圖片,併為其分配記憶體。下面看一下計算inSampleSize的具體實現。


public static int calculateInSampleSize(
            BitmapFactory.Options options, int reqWidth, int reqHeight) {
    // Raw height and width of image
    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;

        // Calculate the largest inSampleSize value that is a power of 2 and keeps both
        // height and width larger than the requested height and width.
        while ((halfHeight / inSampleSize) > reqHeight
                && (halfWidth / inSampleSize) > reqWidth) {
            inSampleSize *= 2;
        }
    }

    return inSampleSize;
}

這裡有兩個問題:

1、  為什麼要先把高寬除以2.

2、  為什麼每次都是inSampleSize*2,也就是為什麼inSampleSize的值是2的冪。

         高寬除以2,是因為存在這麼一種情況,原圖高寬除以2之後,大小比要顯示的高寬要小,這個時候如果inSampleSize*2的話,圖片會有些失真。例如原圖是100*100,而要顯示的圖的大小是80*80,如果設定inSampleSize為2,那麼壓縮後的圖為50*50,50*50的圖片顯示在80*80的ImageView中,會有些失真,所以這個時候inSampleSize應該為1,將100*100的圖片顯示在80*80的ImageView中,就不會失真。

        inSampleSize的值是2的冪,是因為解碼器使用基於2的冪值, inSampleSize的其他任何值將被四捨五入到最近的2的冪(Note: thedecoder uses a final value based on powers of 2, any other value will berounded down to the nearest power of 2.)。所以我們這裡直接就把inSampleSize處理為2的冪值。

當然,計算inSampleSize的方法可以根據實際情況來設定,例如你就是要把圖片壓縮的佔用記憶體更小,不考慮失真,那麼完全可以把inSampleSize設定為一個更大的值。

根據上面的方法,假設想在100*100的ImageView中顯示圖片,就可以這麼寫了:


mImageView.setImageBitmap(
    decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));