1. 程式人生 > >Bitmap和BitmapFactory物件使用

Bitmap和BitmapFactory物件使用

前言

已經有一段時間沒有些部落格了,最經又把關於影象處理的這塊內容有溫習了一遍,總結一下吧!

效果如下
這裡寫圖片描述

Bitmap

Bitmap代表一張點陣圖,BitmapDrawable裡面封裝的圖片就是一個Bitmap物件。開發者為了把一個Bitmap物件包裝成BitmapDrawable對下,可以呼叫BitmapDrawable的構造器:

//獲取一個BitmapDrawable所包裝的Bitmap物件
Bitmap bit = drawable.getBitmap();

除此之外,Bitmap還提供一些靜態方法來建立新的Bitmap物件,例如如下常用方法。

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

    Returns an immutable(不可變) bitmap from the specified subset of the source bitmap
    意思是從原圖的座標點(給點的X,Y)開始,從中“挖取”寬width,高height的一塊出來,建立新的Bitmap物件。

  • static Bitmap createScaledBitmap(Bitmap src, int dstWidth, int dstHeight, boolean filter)

    Creates a new bitmap, scaled from an existing bitmap, when possible.
    建立一個縮放的bitmap物件

  • static Bitmap createBitmap(int[] colors, int width, int height, Bitmap.Config config)

    Returns a immutable bitmap with the specified width and height, with each pixel value set to the corresponding value in the colors array.
    建立一個指定寬高的bitmap物件

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

    建立一張按照Matrix指定規則進行變換的bitmap物件

  • Android官方文件
    這裡寫圖片描述

BitmapFactory

BitmapFatory是一個工具類,他用於提供大量的方法,這些方法可以從不同的資料來源來解析,建立Bitmap物件。BitmapFactory包含了如下方法。

Creates Bitmap objects from various sources, including files, streams, and byte-arrays.

  • static Bitmap decodeByteArray(byte[] data, int offset, int length, BitmapFactory.Options opts)

    Decode an immutable bitmap from the specified byte array.
    從指定位元組陣列的offset開始,將長度為length的位元組資料解析成Bitmap物件

  • static Bitmap decodeByteArray(byte[] data, int offset, int length)

    Decode an immutable bitmap from the specified byte array.
    從指定位元組陣列的offset開始,將長度為length的位元組資料解析成Bitmap物件

  • 官方文件如下
    這裡寫圖片描述

How to use it?

Android為Bitmap提供兩個方法來判斷他是否已被回收了,以及強制Bitmap回收自己。

  • Boolean isRecycled();返回該Bitmap物件是否已被回收
  • void recycle();強制一個Bitmap物件立即回收自己

場景

自擬一個使用場景,在/assets/目錄下面建立一個子目錄imgs,然後再imgs下存放一些圖片,然後做一個圖片檢視器,每點選一次圖片就切換下一張。

注意:在assets目錄下的資源是無法在R.java檔案中建立索引的,所以要藉助AssetManager 來獲取資源

稍微瞭解一下AssetManager物件

//繼承圖如下
public final class
AssetManager
extends Object
java.lang.Objectandroid.content.res.AssetManage

看一下官方的described info吧

Provides access to an application’s raw asset files; see Resources for the way most applications will want to retrieve their resource data. This class presents a lower-level API that allows you to open and read raw files that have been bundled with the application as a simple stream of bytes.
> 大概意思就是可以通過AssetManager獲取應用程式原始的資原始檔,之所以原始,是因為asset目錄下的資源沒有在R.java中進行資源登記,所以要不能在Context的Resource物件中獲取到asset目錄下的檔案,而與此對應的R.java中登記過的資源,可以稱之為非原始檔案,可以通過resource物件獲取之

提供如下方法
這裡寫圖片描述

除此之外我在另外一篇文章中也有對BitmapFactory有介紹

  • public final String[] getLocales ()

Get the locales that this asset manager contains data for.
獲取系統所支援的語言


  • final String[] list(String path)

Return a String array of all the assets at the given path.
獲取path路徑下的資原始檔名字串陣列

try {
    strImagesArray = assetManager.list("images");
    if(strImagesArray==null){
        Log.i("info", "images is null");
    }else{
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < strImagesArray.length; i++) {
            sb.append(strImagesArray[i]).append("\t");
        }
        Log.i("info", sb.toString());
    }   
} catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    Log.i("info", e.toString());
}
![這裡寫圖片描述](https://img-blog.csdn.net/20160404165141495) 獲取到的資料是圖片檔名
  • final InputStream open(String fileName)

    Open an asset using ACCESS_STREAMING mode.
    預設使用ACCESS_STREAMING模式獲取檔案流

  • final InputStream open(String fileName, int accessMode)

    四種Mode,有興趣的自行研究一下
    int ACCESS_BUFFER Mode for open(String, int): Attempt to load contents into memory, for fast small reads.
    int ACCESS_RANDOM Mode for open(String, int): Read chunks, and seek forward and backward.
    int ACCESS_STREAMING Mode for open(String, int): Read sequentially, with an occasional forward seek.
    int ACCESS_UNKNOWN Mode for open(String, int): no specific information about how data will be accessed.

InputStream is = null;
try {
//一定注意完成的資源路徑
    is = assetManager.open("images/"+strImagesArray[currentIndex++]);
} catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    Log.e("error", e.toString());
}
  • final AssetFileDescriptor openFd(String fileName)
  • final AssetFileDescriptor openNonAssetFd(String fileName)
  • final AssetFileDescriptor openNonAssetFd(int cookie, String fileName)
    以上三個方法返回至均是AssetFileDescriptor 物件
public class
AssetFileDescriptor
extends Object
implements Parcelable Closeable
java.lang.Objectandroid.content.res.AssetFileDescriptor

File descriptor of an entry in the AssetManager. This provides your own opened FileDescriptor that can be used to read the data, as well as the offset and length of that entry’s data in the file.
實際意思就是在InputStream open(String fileName, int accessMode)此方法上做了一層包裝,便且是隻讀型別的,從字面意思就可以明白AssetFileDescriptor使資原始檔描述器,封裝了檔案一些屬性資訊的描述,如獲取到檔案的位元組長度等資訊

再瞧一瞧AssetFileDescriptor類是什麼玩意兒吧!
這裡寫圖片描述
可以清晰的知道AssetFileDescriptor就是檔案的詳細描述類

獲取的時候這樣使用

AssetFileDescriptor afd = null;
try {

    afd = assetManager.openFd("images/"
            + strImagesArray[currentIndex++]);

} catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}

怎樣使用這個物件呢,如下所示

if(afd!=null){         
    iv.setImageBitmap(BitmapFactory.
        decodeFileDescriptor(afd.getFileDescriptor()));
}

然而當你這樣寫時候你以為會如你所願,可事實並不這樣

AssetFileDescriptor afd = null;
try {
    afd = assetManager.openFd("images/"
            + strImagesArray[currentIndex++]);
} catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}

// 獲取上一張圖片,並將其回收
BitmapDrawable bd = (BitmapDrawable) iv.getDrawable();
if (bd != null) {
    Bitmap bitmap = bd.getBitmap();
    if (bitmap != null) {
        if (bitmap.isRecycled()) {
            bitmap.recycle();// 對上一張圖片進行回收
        }
    } else {
        Log.i("info", "bitmap is null");
    }
} else {
    Log.i("info", "pre bitmap is null");
}

// iv.setImageBitmap(BitmapFactory.decodeStream(is));
if (afd != null) {
    FileDescriptor fd = afd.getFileDescriptor();
    if (fd != null) { // 不為空
        Bitmap bitmap = BitmapFactory.decodeFileDescriptor(fd); // 返回值為空
        if (bitmap != null) { // 結果為空
            iv.setImageBitmap(bitmap);
        } else {
            Log.i("info",
                    "bitmap is null" + "length="
                            + afd.getDeclaredLength()); // 可以得到真實檔案長度
        }

    } else {
        Log.i("info", "fd is null");
    }
} else {
    Log.i("info", "afd is null");
}

不正確程式碼,可以研究一下)為空完整程式碼如下:

package com.example.android_bitmap_001_1;

import java.io.FileDescriptor;
import java.io.IOException;
import java.io.InputStream;

import android.os.Bundle;
import android.app.Activity;
import android.content.res.AssetFileDescriptor;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageView;

public class MainActivity extends Activity {

    private ImageView iv;
    private int currentIndex;
    private String[] strImagesArray;
    private String[] locals;
    private AssetManager assetManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        iv = (ImageView) findViewById(R.id.iv);

        assetManager = getAssets();

        locals = assetManager.getLocales();
        for (int i = 0; i < locals.length && locals != null; i++) {
            Log.i("info", locals[i]);
        }

        try {
            strImagesArray = assetManager.list("images");
            if (strImagesArray == null) {
                Log.i("info", "images is null");
            } else {
                StringBuffer sb = new StringBuffer();
                for (int i = 0; i < strImagesArray.length; i++) {
                    sb.append(strImagesArray[i]).append("\t");
                }
                Log.i("info", sb.toString());
            }

        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            Log.i("info", e.toString());
        }

        iv.setOnClickListener(new OnClickListener() {

            public void onClick(View v) {
                // TODO Auto-generated method stub
                // 1.每次載入不同的圖片所以,currentIndex每次要變化,同時一定判斷越界
                // 2.載入的圖片一定要判斷後綴是否正確
                // 3.載入圖片並加一

                if (currentIndex >= strImagesArray.length) {
                    currentIndex = 0;
                }

                // 如果下一個檔案的字尾不是該圖片,currentIndex要自加一
                while (!strImagesArray[currentIndex].endsWith("png")
                        && !strImagesArray[currentIndex].endsWith("jpg")
                        && !strImagesArray[currentIndex].endsWith("gif")) {
                    currentIndex++;
                    if (currentIndex >= strImagesArray.length) {
                        currentIndex = 0;
                    }
                }
                // InputStream is = null;
                // try {
                // is = assetManager.open("images/"
                // + strImagesArray[currentIndex++]);
                // } catch (IOException e) {
                // // TODO Auto-generated catch block
                // e.printStackTrace();
                // Log.e("error", e.toString());
                // }

                AssetFileDescriptor afd = null;
                try {
                    afd = assetManager.openFd("images/"
                            + strImagesArray[currentIndex++]);
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }

                // 獲取上一張圖片,並將其回收
                BitmapDrawable bd = (BitmapDrawable) iv.getDrawable();
                if (bd != null) {
                    Bitmap bitmap = bd.getBitmap();
                    if (bitmap != null) {
                        if (bitmap.isRecycled()) {
                            bitmap.recycle();// 對上一張圖片進行回收
                        }
                    } else {
                        Log.i("info", "bitmap is null");
                    }
                } else {
                    Log.i("info", "pre bitmap is null");
                }

                // iv.setImageBitmap(BitmapFactory.decodeStream(is));
                if (afd != null) {
                    FileDescriptor fd = afd.getFileDescriptor();
                    if (fd != null) { // 不為空
                        Bitmap bitmap = BitmapFactory.decodeFileDescriptor(fd); // 返回值為空
                        if (bitmap != null) { // 結果為空
                            iv.setImageBitmap(bitmap);
                        } else {
                            Log.i("info",
                                    "bitmap is null" + "length="
                                            + afd.getDeclaredLength()); // 可以得到真實檔案長度
                        }

                    } else {
                        Log.i("info", "fd is null");
                    }
                } else {
                    Log.i("info", "afd is null");
                }
            }
        });

    }

}

最後來來看Bitmap和BitmapFactory最基本的使用方法吧

//獲取上一張圖片,並將其回收
BitmapDrawable bd = (BitmapDrawable) iv.getDrawable();
if(bd!=null){
    Bitmap bitmap = bd.getBitmap();
    if(bitmap!=null){
        if(bitmap.isRecycled()){
            bitmap.recycle();// 對上一張圖片進行回收
        }
    }else{
        Log.i("info", "bitmap is null");
    }
}else{
    Log.i("info", "pre bitmap is null");
}

iv.setImageBitmap(BitmapFactory.decodeStream(is));

正確結果程式碼)最後完整程式碼如下

package com.example.android_bitmap_001_1;

import java.io.IOException;
import java.io.InputStream;

import android.os.Bundle;
import android.app.Activity;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageView;

public class MainActivity extends Activity {

    private ImageView iv;
    private int currentIndex;
    private String[] strImagesArray;
    private String[] locals;
    private AssetManager assetManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        iv = (ImageView) findViewById(R.id.iv);

        assetManager = getAssets();

        locals = assetManager.getLocales();
        for (int i = 0; i < locals.length && locals != null; i++) {
            Log.i("info", locals[i]);
        }

        try {
            strImagesArray = assetManager.list("images");
            if (strImagesArray == null) {
                Log.i("info", "images is null");
            } else {
                StringBuffer sb = new StringBuffer();
                for (int i = 0; i < strImagesArray.length; i++) {
                    sb.append(strImagesArray[i]).append("\t");
                }
                Log.i("info", sb.toString());
            }

        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            Log.i("info", e.toString());
        }

        iv.setOnClickListener(new OnClickListener() {

            public void onClick(View v) {
                // TODO Auto-generated method stub
                // 1.每次載入不同的圖片所以,currentIndex每次要變化,同時一定判斷越界
                // 2.載入的圖片一定要判斷後綴是否正確
                // 3.載入圖片並加一

                if (currentIndex >= strImagesArray.length) {
                    currentIndex = 0;
                }

                // 如果下一個檔案的字尾不是該圖片,currentIndex要自加一
                while (!strImagesArray[currentIndex].endsWith("png")
                        && !strImagesArray[currentIndex].endsWith("jpg")
                        && !strImagesArray[currentIndex].endsWith("gif")) {
                    currentIndex++;
                    if (currentIndex >= strImagesArray.length) {
                        currentIndex = 0;
                    }
                }
                InputStream is = null;
                try {
                    is = assetManager.open("images/"
                            + strImagesArray[currentIndex++]);
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                    Log.e("error", e.toString());
                }

                // 獲取上一張圖片,並將其回收
                BitmapDrawable bd = (BitmapDrawable) iv.getDrawable();
                if (bd != null) {
                    Bitmap bitmap = bd.getBitmap();
                    if (bitmap != null) {
                        if (bitmap.isRecycled()) {
                            bitmap.recycle();// 對上一張圖片進行回收
                        }
                    } else {
                        Log.i("info", "bitmap is null");
                    }
                } else {
                    Log.i("info", "pre bitmap is null");
                }

                iv.setImageBitmap(BitmapFactory.decodeStream(is));
            }
        });

    }

}