玩轉Android Bitmap

玩轉Android Bitmap


1. 初識Bitmap


Bitmap是一個final類,因此不能被繼承。Bitmap只有一個構造方法,且該構造方法是沒有任何訪問權限修飾符修飾,也就是說該構造方法是friendly,但是谷歌稱Bitmap的構造方法是private(私有的),感覺有點不嚴謹。不管怎樣,一般情況下,我們不能通過構造方法直接新建一個Bitmap對象。
Bitmap是Android系統中的圖像處理中最重要類之一。Bitmap可以獲取圖像文件信息,對圖像進行剪切、旋轉、縮放,壓縮等操作,并可以以指定格式保存圖像文件。

2. 創建Bitmap對象


既然不能直接通過構造方法創建Bitmap,那怎樣才能創建Bitmap對象。通常我們可以利用Bitmap的靜態方法createBitmap()和BitmapFactory的decode系列靜態方法創建Bitmap對象。

  • Bitmap的靜態方法createBitmap()

  • BitmapFactory的decode系列靜態方法

3. Bitmap的顏色配置信息與壓縮方式信息


Bitmap中有兩個內部枚舉類:ConfigCompressFormatConfig是用來設置顏色配置信息的,CompressFormat是用來設置壓縮方式的。

  • Config解析:
    • Bitmap.Config.ALPHA_8:顏色信息只由透明度組成,占8位。
    • Bitmap.Config.ARGB_4444:顏色信息由透明度與R(Red),G(Green),B(Blue)四部分組成,每個部分都占4位,總共占16位。
    • Bitmap.Config.ARGB_8888:顏色信息由透明度與R(Red),G(Green),B(Blue)四部分組成,每個部分都占8位,總共占32位。是Bitmap默認的顏色配置信息,也是最占空間的一種配置。
    • Bitmap.Config.RGB_565:顏色信息由R(Red),G(Green),B(Blue)三部分組成,R占5位,G占6位,B占5位,總共占16位。

通常我們優化Bitmap時,當需要做性能優化或者防止OOM(Out Of Memory),我們通常會使用Bitmap.Config.RGB_565這個配置,因為Bitmap.Config.ALPHA_8只有透明度,顯示一般圖片沒有意義,Bitmap.Config.ARGB_4444顯示圖片不清楚,Bitmap.Config.ARGB_8888占用內存最多。

  • CompressFormat解析:
    • Bitmap.CompressFormat.JPEG:表示以JPEG壓縮算法進行圖像壓縮,壓縮后的格式可以是".jpg"或者".jpeg",是一種有損壓縮。
    • Bitmap.CompressFormat.PNG:表示以PNG壓縮算法進行圖像壓縮,壓縮后的格式可以是".png",是一種無損壓縮。
    • Bitmap.CompressFormat.WEBP:表示以WebP壓縮算法進行圖像壓縮,壓縮后的格式可以是".webp",是一種有損壓縮,質量相同的情況下,WebP格式圖像的體積要比JPEG格式圖像小40%。美中不足的是,WebP格式圖像的編碼時間“比JPEG格式圖像長8倍”。

4. Bitmap對圖像進行操作


1. Bitmap裁剪圖像

Bitmap裁剪圖像有兩種方式:

  • Bitmap.createBitmap(Bitmap source, int x, int y, int width, int height)

根據源Bitmap對象source,創建出source對象裁剪后的圖像的Bitmap。x,y分別代表裁剪時,x軸和y軸的第一個像素,width,height分別表示裁剪后的圖像的寬度和高度。
注意:x+width要小于等于source的寬度,y+height要小于等于source的高度。

  • Bitmap.createBitmap(Bitmap source, int x, int y, int width, int height,Matrix m, boolean filter)

這個方法只比上面的方法多了mfilter這兩個參數,m是一個Matrix(矩陣)對象,可以進行縮放,旋轉,移動等動作,filter為true時表示source會被過濾,僅僅當m操作不僅包含移動操作,還包含別的操作時才適用。其實上面的方法本質上就是調用這個方法而已。

    public static Bitmap createBitmap(Bitmap source, int x, int y, int width, int height) {
        return createBitmap(source, x, y, width, height, null, false);
    }  

2. Bitmap縮放,旋轉,移動圖像

Bitmap縮放,旋轉,移動,傾斜圖像其實就是通過Bitmap.createBitmap(Bitmap source, int x, int y, int width, int height,Matrix m, boolean filter)方法實現的,只是在實現這些功能的同時還可以實現圖像的裁剪。

        // 定義矩陣對象  
        Matrix matrix = new Matrix();  
        // 縮放圖像  
        matrix.postScale(0.8f, 0.9f);  
        // 向左旋轉(逆時針旋轉)45度,參數為正則向右旋轉(順時針旋轉) 
        matrix.postRotate(-45);  
        //移動圖像
        //matrix.postTranslate(100,80);
        Bitmap bitmap = Bitmap.createBitmap(source, 0, 0, source.getWidth(), source.getHeight(),  
                matrix, true);    

Matrix的postScalepostRotate方法還有多帶兩個參數的重載方法postScale(float sx, float sy, float px, float py)postRotate(float degrees, float px, float py),后兩個參數pxpy都表示以該點為中心進行操作。
注意:雖然Matrix還可以調用postSkew方法進行傾斜操作,但是卻不可以在此時創建Bitmap時使用。

3. Bitmap保存圖像與釋放資源

        bitmap=BitmapFactory.decodeResource(getResources(),R.drawable.feng);
        File file=new File(getFilesDir(),"lavor.jpg");
        if(file.exists()){
            file.delete();
        }
        try {
            FileOutputStream outputStream=new FileOutputStream(file);
            bitmap.compress(Bitmap.CompressFormat.JPEG,90,outputStream);
            outputStream.flush();
            outputStream.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        bitmap.recycle();//釋放bitmap的資源,這是一個不可逆轉的操作

5. BitmapFactory通過BitmapFactory.Options對圖像進行操作


BitmapFactory是通過BitmapFactory.Options對圖像進行操作的,然后將操作后的圖像生成Bitmap對象或者將操作后的圖像用已經存在的Bitmap保存,當不能用之保存時會返回null
BitmapFactory.Options中常用的字段有:

  • inBitmap:如果設置將會將生成的圖像內容加載到該Bitmap對象中。
  • inDensity:給Bitmap對象設置的密度,如果inScaled為true(這是默認的),而若inDensityinTargetDensity不匹配,那么就會在Bitmap對象返回前將其縮放到匹配inTargetDensity
  • inDither:是否對圖像進行抖動處理,默認值是false。
  • inJustDecodeBounds:如果設置成true,表示獲取Bitmap對象信息,但是不將其像素加載到內存。
  • inPreferredConfig:Bitmap對象顏色配置信息,默認是Bitmap.Config.ARGB_8888
  • inSampleSize:對圖像進行壓縮,設置的值為2的整數次冪或者接近2的整數次冪,當次設置為2時,寬和高為都原來的1/2,圖像所占空間為原來的1/4。
  • inScaled:設置是否縮放。
  • inTargetDensity:繪制到目標Bitmap上的密度。
  • outHeight:Bitmap對象的高度。
  • outWidth:Bitmap對象的寬度。

6. 使用Bitmap時防止OOM的有效方法


1. 高效壓縮圖片

    /**
     * 谷歌推薦使用方法,從資源中加載圖像,并高效壓縮,有效降低OOM的概率
     * @param res 資源
     * @param resId 圖像資源的資源id
     * @param reqWidth 要求圖像壓縮后的寬度
     * @param reqHeight 要求圖像壓縮后的高度
     * @return
     */
    public Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight) {
        // 設置inJustDecodeBounds = true ,表示獲取圖像信息,但是不將圖像的像素加入內存
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeResource(res, resId, options);

        // 調用方法計算合適的 inSampleSize
        options.inSampleSize = calculateInSampleSize(options, reqWidth,
                reqHeight);

        // inJustDecodeBounds 置為 false 真正開始加載圖片
        options.inJustDecodeBounds = false;
        //將options.inPreferredConfig改成Bitmap.Config.RGB_565,
        // 是默認情況Bitmap.Config.ARGB_8888占用內存的一般
        options.inPreferredConfig= Bitmap.Config.RGB_565;
        return BitmapFactory.decodeResource(res, resId, options);
    }

    // 計算 BitmapFactpry 的 inSimpleSize的值的方法
    public int calculateInSampleSize(BitmapFactory.Options options,
                                     int reqWidth, int reqHeight) {
        if (reqWidth == 0 || reqHeight == 0) {
            return 1;
        }

        // 獲取圖片原生的寬和高
        final int height = options.outHeight;
        final int width = options.outWidth;
        Log.d(TAG, "origin, w= " + width + " h=" + height);
        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;
            }
        }

        Log.d(TAG, "sampleSize:" + inSampleSize);
        return inSampleSize;
    }  

2. 使用緩存

常用的緩存有內存緩存LruCache和磁盤緩存DiskLruCache

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,333評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,491評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,263評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,946評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,708評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,186評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,409評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,939評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,774評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,976評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,209評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,641評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,872評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,650評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,958評論 2 373

推薦閱讀更多精彩內容