有效的處理較大的點陣圖

yangxi_001發表於2013-11-27

影象有各種形狀和大小。在許多情況下,他們往往比一個典型應用程式的使用者介面(UI)所需要的資源更大。例如,系統的Gallery程式展示使用Android裝置照相機所拍攝的照片通常要比你的裝置的螢幕密度更高的解析度下顯示。

既然你所使用的記憶體有限,理想狀況下你只想在記憶體中載入一個低版本的方案。低版本的方案應該匹配顯示它的UI元件的大小。一個更高的解決方案不提供任何可見的好處,但是仍然佔用以前的記憶體,由於額外的縮放會導致額外的效能開銷。

這節課將引導你在不超過記憶體限制的情況下通過解碼大點陣圖,在記憶體中載入一個較小的點陣圖子樣本。

BitmapFactory類提供了集中解碼的方法(decodeByteArray(),decodeFile(),decodeResource(),等等)從多種資源中來建立一個點陣圖。選擇最合適的解碼方法基於你的影象資料資源。這些方法試圖情去分配記憶體來構造點陣圖,因此很容易導致OutOfMemory異常。每種型別的解碼方法都有額外的特徵可以讓你通過BitMapFactory.Options類指定特定解碼方法。當解碼時避免記憶體分配可以設定inJustDecodeBounds屬性為true,點陣圖物件返回null除非設定outWidth,outHeight和outMimeType。這種技術允許你在建立點陣圖(分配記憶體)之前去讀取影象的維度和型別。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
     BitmapFactory.Options options = new BitmapFactory.Options();

    options.inJustDecodeBounds = true;

    BitmapFactory.decodeResource(getResources(), R.id.myimage, options);

    int imageHeight = options.outHeight;

    int imageWidth = options.outWidth;

    String imageType = options.outMimeType;

為了避免java.lang.OutOfMemory異常,在解碼點陣圖之前請檢查它的維度,除非你十分確定資源提供給你的可預見的影象資料正好滿足你的記憶體。

現在的影象尺寸都是已知的,他們可以被用來決定是否應該載入到儲存器或者是否一個子樣本的版本被載入。以下是一些值得考慮的因素:

    • 估計載入完整影象所需要的記憶體;
    • 你承諾載入這個圖片所需空間帶給你的程式的其他記憶體需求;
    • 準備載入影象的目標ImageView或UI組建尺寸;
    • 當前裝置的螢幕尺寸和密度;

例如,如果1024* 768畫素的影象最終被縮略地顯示在一個128* 96畫素的ImageView中,就不值得載入到記憶體中去。

告訴解碼器去子樣本化影象,載入一個更小的版本到記憶體中,在你的BitmapFactory.Option物件中設定inSampleSize為true。例如,將一個解析度為2048* 1536的影象解碼為4個子版本大小為大約512* 384的點陣圖。載入這個到記憶體中僅使用0.75MB,而不是完整的12MB大小的影象(假設使用ARGB_8888點陣圖的配置)。這裡有一個方法在目標的寬度和高度的基礎上來計算一個樣本的大小。
```Java
public static int calculateInSampleSize(

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
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) {
 if (width > height) {
 inSampleSize = Math.round((float)height / (float)reqHeight);
 } else {
 inSampleSize = Math.round((float)width / (float)reqWidth);
 }
 }
 return inSampleSize;
 }
 <p style="border-color: #258AAF; border-style: solid; border-width: 0 0 0 4px; padding: 0 0 0 10px;">
 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
**NOTE:**使用2的冪數設定inSampleSize的值可以使解碼器更快,更有效。然而,如果你想在記憶體或硬碟中快取一個圖片調整後的版本,通常解碼到合適的影象尺寸更適合來節省空間。

要使用這種方法,首先解碼,將inJustDecodeBounds設定為true,將選項傳遞進去,然後再次解碼,在使用新的inSampleSize值並將inJustDecodeBounds設定為false:
     
   ```Java
    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);

       }

這種方法可以很容易地載入任意大小的點陣圖到一個ImageView顯示一個100x100畫素的縮圖,如下面的示例程式碼所示:
```Java

1
2
3
mImageView.setImageBitmap(

decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
你可以按照類似的過程,用適當的BitmapFactory.decode* 中的方法去解碼一個從其他資源得到的點陣圖。影象有各種形狀和大小。在許多情況下,他們往往比一個典型應用程式的使用者介面(UI)所需要的資源更大。例如,系統的Gallery程式展示使用Android裝置照相機所拍攝的照片通常要比你的裝置的螢幕密度更高的解析度下顯示。

既然你所使用的記憶體有限,理想狀況下你只想在記憶體中載入一個低版本的方案。低版本的方案應該匹配顯示它的UI元件的大小。一個更高的解決方案不提供任何可見的好處,但是仍然佔用以前的記憶體,由於額外的縮放會導致額外的效能開銷。

這節課將引導你在不超過記憶體限制的情況下通過解碼大點陣圖,在記憶體中載入一個較小的點陣圖子樣本。

BitmapFactory類提供了集中解碼的方法(decodeByteArray(),decodeFile(),decodeResource(),等等)從多種資源中來建立一個點陣圖。選擇最合適的解碼方法基於你的影象資料資源。這些方法試圖情去分配記憶體來構造點陣圖,因此很容易導致OutOfMemory異常。每種型別的解碼方法都有額外的特徵可以讓你通過BitMapFactory.Options類指定特定解碼方法。當解碼時避免記憶體分配可以設定inJustDecodeBounds屬性為true,點陣圖物件返回null除非設定outWidth,outHeight和outMimeType。這種技術允許你在建立點陣圖(分配記憶體)之前去讀取影象的維度和型別。

```Java
     BitmapFactory.Options options = new BitmapFactory.Options();

    options.inJustDecodeBounds = true;

    BitmapFactory.decodeResource(getResources(), R.id.myimage, options);

    int imageHeight = options.outHeight;

     int imageWidth = options.outWidth;

    String imageType = options.outMimeType;

為了避免java.lang.OutOfMemory異常,在解碼點陣圖之前請檢查它的維度,除非你十分確定資源提供給你的可預見的影象資料正好滿足你的記憶體。

現在的影象尺寸都是已知的,他們可以被用來決定是否應該載入到儲存器或者是否一個子樣本的版本被載入。以下是一些值得考慮的因素:

    • 估計載入完整影象所需要的記憶體;
    • 你承諾載入這個圖片所需空間帶給你的程式的其他記憶體需求;
    • 準備載入影象的目標ImageView或UI組建尺寸;
    • 當前裝置的螢幕尺寸和密度;

例如,如果1024* 768畫素的影象最終被縮略地顯示在一個128* 96畫素的ImageView中,就不值得載入到記憶體中去。

告訴解碼器去子樣本化影象,載入一個更小的版本到記憶體中,在你的BitmapFactory.Option物件中設定inSampleSize為true。例如,將一個解析度為2048* 1536的影象解碼為4個子版本大小為大約512* 384的點陣圖。載入這個到記憶體中僅使用0.75MB,而不是完整的12MB大小的影象(假設使用ARGB_8888點陣圖的配置)。這裡有一個方法在目標的寬度和高度的基礎上來計算一個樣本的大小。

 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
     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) {

    if (width > height) {

    inSampleSize = Math.round((float)height / (float)reqHeight);

    } else {

    inSampleSize = Math.round((float)width / (float)reqWidth);

    }
    }
    return inSampleSize;
    }

NOTE:使用2的冪數設定inSampleSize的值可以使解碼器更快,更有效。然而,如果你想在記憶體或硬碟中快取一個圖片調整後的版本,通常解碼到合適的影象尺寸更適合來節省空間。

要使用這種方法,首先解碼,將inJustDecodeBounds設定為true,將選項傳遞進去,然後再次解碼,在使用新的inSampleSize值並將inJustDecodeBounds設定為false:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
     public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
     
    int reqWidth, int reqHeight) {

    // First decode with inJustDecodeBounds=true to check dime

    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);

    }

這種方法可以很容易地載入任意大小的點陣圖到一個ImageView顯示一個100x100畫素的縮圖,如下面的示例程式碼所示:

1
2
3
    mImageView.setImageBitmap(

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

你可以按照類似的過程,用適當的BitmapFactory.decode* 中的方法去解碼一個從其他資源得到的點陣圖。

相關文章