Android怎么高效加載大圖

2018-08-02 17:35 更新

編寫:kesenhoo - 原文:http://developer.android.com/training/displaying-bitmaps/load-bitmap.html

圖片有不同的形狀與大小。在大多數(shù)情況下它們的實(shí)際大小都比需要呈現(xiàn)的尺寸大很多。例如,系統(tǒng)的圖庫(kù)應(yīng)用會(huì)顯示那些我們使用相機(jī)拍攝的照片,但是那些圖片的分辨率通常都比設(shè)備屏幕的分辨率要高很多。

考慮到應(yīng)用是在有限的內(nèi)存下工作的,理想情況是我們只需要在內(nèi)存中加載一個(gè)低分辨率的照片即可。為了更便于顯示,這個(gè)低分辨率的照片應(yīng)該是與其對(duì)應(yīng)的UI控件大小相匹配的。加載一個(gè)超過屏幕分辨率的高分辨率照片不僅沒有任何顯而易見的好處,還會(huì)占用寶貴的內(nèi)存資源,另外在快速滑動(dòng)圖片時(shí)容易產(chǎn)生額外的效率問題。

這一課會(huì)介紹如何通過加載一個(gè)縮小版本的圖片,從而避免超出程序的內(nèi)存限制。

讀取位圖的尺寸與類型(Read Bitmap Dimensions and Type)

BitmapFactory提供了一些解碼(decode)的方法(decodeByteArray()decodeFile()decodeResource()等),用來從不同的資源中創(chuàng)建一個(gè)Bitmap。 我們應(yīng)該根據(jù)圖片的數(shù)據(jù)源來選擇合適的解碼方法。 這些方法在構(gòu)造位圖的時(shí)候會(huì)嘗試分配內(nèi)存,因此會(huì)容易導(dǎo)致OutOfMemory的異常。每一種解碼方法都可以通過BitmapFactory.Options設(shè)置一些附加的標(biāo)記,以此來指定解碼選項(xiàng)。設(shè)置 inJustDecodeBounds 屬性為true可以在解碼的時(shí)候避免內(nèi)存的分配,它會(huì)返回一個(gè)null的Bitmap,但是可以獲取到 outWidth, outHeight 與 outMimeType。該技術(shù)可以允許你在構(gòu)造Bitmap之前優(yōu)先讀圖片的尺寸與類型。

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 的異常,我們需要在真正解析圖片之前檢查它的尺寸(除非你能確定這個(gè)數(shù)據(jù)源提供了準(zhǔn)確無誤的圖片且不會(huì)導(dǎo)致占用過多的內(nèi)存)。

加載一個(gè)按比例縮小的版本到內(nèi)存中(Load a Scaled Down Version into Memory)

通過上面的步驟我們已經(jīng)獲取到了圖片的尺寸,這些數(shù)據(jù)可以用來幫助我們決定應(yīng)該加載整個(gè)圖片到內(nèi)存中還是加載一個(gè)縮小的版本。有下面一些因素需要考慮:

  • 評(píng)估加載完整圖片所需要耗費(fèi)的內(nèi)存。
  • 程序在加載這張圖片時(shí)可能涉及到的其他內(nèi)存需求。
  • 呈現(xiàn)這張圖片的控件的尺寸大小。
  • 屏幕大小與當(dāng)前設(shè)備的屏幕密度。

例如,如果把一個(gè)大小為1024x768像素的圖片顯示到大小為128x96像素的ImageView上嗎,就沒有必要把整張?jiān)瓐D都加載到內(nèi)存中。

為了告訴解碼器去加載一個(gè)縮小版本的圖片到內(nèi)存中,需要在BitmapFactory.Options 中設(shè)置 inSampleSize 的值。例如, 一個(gè)分辨率為2048x1536的圖片,如果設(shè)置 inSampleSize 為4,那么會(huì)產(chǎn)出一個(gè)大約512x384大小的Bitmap。加載這張縮小的圖片僅僅使用大概0.75MB的內(nèi)存,如果是加載完整尺寸的圖片,那么大概需要花費(fèi)12MB(前提都是Bitmap的配置是 ARGB_8888)。下面有一段根據(jù)目標(biāo)圖片大小來計(jì)算Sample圖片大小的代碼示例:

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

Note: 設(shè)置inSampleSize為2的冪是因?yàn)榻獯a器最終還是會(huì)對(duì)非2的冪的數(shù)進(jìn)行向下處理,獲取到最靠近2的冪的數(shù)。詳情參考inSampleSize的文檔。

為了使用該方法,首先需要設(shè)置 inJustDecodeBounds 為 true, 把options的值傳遞過來,然后設(shè)置 inSampleSize 的值并設(shè)置 inJustDecodeBounds 為 false,之后重新調(diào)用相關(guān)的解碼方法。

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

使用上面這個(gè)方法可以簡(jiǎn)單地加載一張任意大小的圖片。如下面的代碼樣例顯示了一個(gè)接近 100x100像素的縮略圖:

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

我們可以通過替換合適的BitmapFactory.decode* 方法來實(shí)現(xiàn)一個(gè)類似的方法,從其他的數(shù)據(jù)源解析Bitmap。


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)