高效的加載圖片1.縮放大容量的Bitmap

我們通過眼睛來觀察世界,眼睛通過光的反射,折射將世間的萬物映射到我們

的眼睛裏。至於是在眼睛裏生成圖片傳遞給大腦,還是眼睛將映射得來的
這些圖片,瞭解了世界。
圖片何其重要,幾乎每一個app都需要加載圖片,然而並不是每一個app
都很好的加載了圖片。
圖片是什麼,我們先說一下圖片的屬性:形狀,大小,顏色。
這一節裏我們討論的是圖片最簡單的屬性:大小。
每一部安卓手機對於加載圖片使用的內存都是有限制的,據我所知,通常
的都是16MB。然而,就像世界上沒有一模一樣的葉子一樣。圖片也是多種
多樣的,它們大小不一,形狀各異。那麼如果超過16MB會發生什麼錯誤呢
你的手機夠舊,配置夠低,那麼很容易的就會報Out of memory的異常,也就是一些人講的OOM異常。
也許你會發現你的圖片並沒有超過16MB啊,爲何還是報錯,那是因爲手
機在加載圖片的時候需要的內存可能會比圖片本身大,(這應該是涉及到圖
片的複雜程度吧)。
如何避免OOM異常呢,手機內存的限制通過軟件是無法改變的,那麼我
們只好在圖片本身上下文章。圖片並不是一成不變的,只要我們能通過一定
的比例縮放圖片就不需要那麼大的內存了。一句話概括就是我們要儘量不破
壞原圖表達的信息的基礎上減小加載圖片所需要的內存。而BitmapFactory.Options這個類就是爲此而生的。

    package com.example.loadbigbitmap;

import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;

public class ImageUtil {
    /**
     * 
     * @param options
     *            根據原圖獲得的options,通過它可以獲得原圖的高,寬,類型。因爲inJustDecodeBounds設置成true了,
     *            所以並不會真的解碼生成一個bitmap,也就不存在佔用內存的問題了。
     * @param reqWidth 要求的寬
     * @param reqHeight 要求的高
     * @return 返回設置好的inSampleSize。
     */
    /*
     * 根據原圖的高度和寬度和要求的高度和寬度計算出inSampleSize的大小。
     * 通過設置inSampleSize可以修改顯示該圖片需要的內存大小。比如原來需要20M
     * ,那麼如果inSampleSize=5,則實際顯示的時候只需要5M就夠了。 
     */
    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;
    }

    /**
     * 
     * @param res 使用getResources()即可
     * @param resId 圖片的id
     * @param reqWidth 要求的高度
     * @param reqHeight 要求的寬度
     * @return 將會返回一個已經根據要求的高度和寬度縮放的bitmap
     */
    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();
        /*
         * 如果我們把它設爲true,那麼BitmapFactory.decodeFile(String path, Options
         * opt)並不會真的返回一個Bitmap給你,它僅僅會把它的寬,高取回來給你,這樣就不會佔用太多的內存,也就不會那麼頻繁的發生OOM了。
         */
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeResource(res, resId, options);

        // Calculate inSampleSize
        options.inSampleSize = calculateInSampleSize(options, reqWidth,
                reqHeight);
        // Decode bitmap with inSampleSize set

        // 把inJustDecodeBounds設置爲false,這樣通過BitmapFactory生成bitmap時就會返回一個真的bitmap而不是null;
        options.inJustDecodeBounds = false;
        //根據圖片的來源確定到底使用哪一種生成BitmapFactory的方法。
        return BitmapFactory.decodeResource(res, resId, options);
    }

}

MainActivity

package com.example.loadbigbitmap;

import android.app.Activity;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.widget.ImageView;

public class MainActivity extends Activity {
    /*
     * 顯示圖片
     */
    private ImageView image;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        intiUI();
        /*
         * 圖片的原始寬度,高度,類型: 1024;768;image/jpeg
         */

        Bitmap bitmap = ImageUtil.decodeSampledBitmapFromResource(getResources(), R.drawable.default_bg, 100, 100);
        image.setImageBitmap(bitmap);

    }

    private void intiUI() {
        image = (ImageView) findViewById(R.id.image);

    }
}
接下來我就會一步一步的從bitmap的生成到BitmapFactory.Options類的
對象的屬性的設置。來講解如何縮放大容量Bitmap。
BitmapFactory有很多方式生成Bitmap。(decodeByteArray(), decodeFile(), decodeResource(), etc.)
 這裏我們使用decodeResource()方法生成bitmap,其他方法大同小異,可以根據自己的需求修改。
    BitmapFactory.decodeResource(getResources(),R.drawable.default_bg, options);

第一個參數是Resources,直接getResources()就行了,第二個參數是圖片的id,第三個參數就是BitmapFactory.Options了,它通過:

final BitmapFactory.Options options = new BitmapFactory.Options();

來獲取。

通過options.inSampleSize的設置控制顯示圖片所需要的大小
整篇文章所需要做的就是設置這個值,假設inSampleSize=5.則根據這個options作爲參數,加載的圖片的內存就會是原來的1/5。

我們應該根據圖片原有的高度,寬度,與需要顯示的高度寬度的比例來計算出這個inSampleSize的大小,如何計算請參考代碼ImageUtil類裏的calculateInSampleSize方法。
假設需要顯示的高度和寬度已經知道了,如何取得圖片原有的高度和寬度呢?
這時候我們就需要先設置options.inJustDecodeBounds = true;設置成true後再執行BitmapFactory.decodeResource(res, resId, options);方法則安卓系統並不會真的去解碼生成一個bitmap,而是返回一個NULL,但是這執行完後的options就會擁有圖片的高度,寬度,類型等屬性了。
有了這個屬性,再執行

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

就對inSampleSize 的值設置成功了。
這時候再執行

        // 把inJustDecodeBounds設置爲false,這樣通過BitmapFactory生成bitmap時就會返回一個真的bitmap而不是null;
        options.inJustDecodeBounds = false;
        //根據圖片的來源確定到底使用哪一種生成BitmapFactory的方法。
        return BitmapFactory.decodeResource(res, resId, options);

便能得到一個縮小之後的bitmap。且不會再佔用很大的內存。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章