Bitmap(點陣圖)全解析 Part 1


本節引言:

在上一節我們對Android中的13種類型的Drawable的型別進行了講解,有沒有應用到自己的 項目當中呢?而本節我們來探討的是Bitmap(點陣圖)的一些使用,而在開始本節的內容之前我們 先來區分幾個名詞的概念:

  • Drawable:通用的圖形對象,用來裝載常用格式的圖像,既可以是PNG,JPG這樣的圖像, 也是前面學的13種Drawable類型的視覺化對象!我們可以理解成一個用來放畫的-畫框
  • Bitmap(點陣圖):我們可以把他看成一個畫架,我們先把畫放到上面,然後我們可以 進行一些處理,例如獲取圖像文件信息,做旋轉切割,放大縮小等操作!
  • Canvas(畫布):如其名,畫布,我們可以在上面作畫(繪製),你既可以用Paint(畫筆), 來畫各種形狀或寫字,又可以用Path(路徑)來繪製多個點,然後連接成各種圖形!
  • Matrix(矩陣):用於圖形特效處理的,顏色矩陣(ColorMatrix),還有使用Matrix進行影像的 平移,縮放,旋轉,傾斜等!

而上述的這些都是Android中的底層圖形類別:android.graphics提供給我們的介面! 嗯,話不多說開始本節內容! PS:官方文件:Bitmap


1.了解Bitmap,BitmapFactory,BitmapFacotry.Options

如題,本來可以直接說著三個東東的關係的,但是我就是要傲嬌,就要看程式碼! 1.gif如果你開啟Bitmap類別的源碼,你會看到Bitmap的建構方法上有這樣一段東東:

2.png

##大概想說的就是:Bitmap的建構方法是私有的,外面不能實例化,只能透過JNI實例化! 當然,肯定也會提供我們一個介面給我們來建立Bitmap的,而這個介面類別就是:

BitmapFactory! 來來來,打開BitmapFactory類,我們點下左邊的Structure可以看到BitmapFactory給我們 提供了這些方法,大部分都是decodeXxx,透過各種形式來創建Bitmap的!

3.png

接著我們又發現了,每種方法,都會有一個Options類型的參數,點進去看看: 於是乎我們發現了這貨是一個靜態內部類別:

BitmapFacotry.Options! 而他是用來設定decode時的選項的!

4.png

我們對這裡的某些參數的值進行設置,例如inJustDecodeBounds設定為true避免OOM(記憶體溢出), 什麼,不知道OOM,沒事,等下一點點跟你說清楚!最後回到我們的Bitmap!嗯,Bitmap中的 方法比較多,就不一一進行講解了,我們從中挑幾個用得較多的來講解! 中文文件:Android中文API(136)— Bitmap


#2.Bitmap常用方法

普通方法

  • public boolean compress (Bitmap.CompressFormat format, int quality, OutputStream stream) 將點陣圖的壓縮到指定的OutputStream,可以理解成將Bitmap儲存到檔案中! format:格式,PNG,JPG等;quality:壓縮質量,0-100,0表示最低畫質壓縮,100最大質量(PNG無損,會忽略品質設定) stream:輸出流 傳回值代表是否成功壓縮到指定流!
  • void recycle():回收位圖所佔用的記憶體空間,標記位圖為Dead
  • boolean isRecycled():判斷位圖記憶體是否已釋放
  • int getWidth():取得位圖的寬度
  • int getHeight():取得位圖的高度
  • boolean isMutable():圖片是否可修改
  • int getScaledWidth(Canvas canvas):取得指定密度轉換後的圖片的寬度
  • int getScaledHeight(Canvas canvas):取得指定密度轉換後的圖片的高度

靜態方法

  • #Bitmap createBitmap(Bitmap src):以src為原圖產生不可變得新映像
  • Bitmap createScaledBitmap(Bitmap src, int dstWidth,int dstHeight , boolean filter):以src為原圖,建立新的影像,指定新影像的高寬以及是否變。
  • Bitmap createBitmap(int width, int height, Config config):建立指定格式、大小的點陣圖
  • Bitmap createBitmap(Bitmap source , int x, int y, int width, int height)以source為原圖,建立新的圖片,指定起始座標以及新影像的高寬。
  • public static Bitmap createBitmap(Bitmap source, int x, int y, int width, int height, Matrix m, boolean filter)

BitmapFactory.Option可設定參數:

  • boolean inJustDecodeBounds-如果設定為true,不取得圖片,不分配內存,但會傳回圖片的高寬度資訊。
  • int inSampleSize——圖片縮放的倍數。如果設為4,則寬和高都為原來的1/4,則圖是原來的1/16。
  • int outWidth—取得圖片的寬度值
  • int outHeight—取得圖片的高度值
  • int inDensity—用於位圖的像素壓縮比
  • int inTargetDensity—用於目標位圖的像素壓縮比(要產生的位圖)
  • boolean inScaled-設定為true時進行圖片壓縮,從inDensity到inTargetDensity。

好吧,就貼這麼多吧,要用自己查文檔~


3.取得Bitmap位圖

從資源中取得位圖的方式有兩種:透過BitmapDrawable或BitmapFactory,下面示範: 我們首先得得到這個

BitmapDrawable方法

你可以建立一個建構一個BitmapDrawable對象,例如透過流來建構BitmapDrawable:

BitmapDrawable bmpMeizi = new BitmapDrawable(getAssets().open("pic_meizi.jpg"));
Bitmap mBitmap = bmpMeizi.getBitmap();
img_bg.setImageBitmap(mBitmap);

BitmapFactory方法

都是靜態方法,直接調,可以透過資源ID、路徑、檔案、資料流等方式來取得位圖!

//通过资源ID
private Bitmap getBitmapFromResource(Resources res, int resId) {
      return BitmapFactory.decodeResource(res, resId);
}

//文件
private Bitmap getBitmapFromFile(String pathName) {
      return BitmapFactory.decodeFile(pathName);
}

//字节数组
public Bitmap Bytes2Bimap(byte[] b) {
    if (b.length != 0) {
        return BitmapFactory.decodeByteArray(b, 0, b.length);
    } else {
        return null;
    }
}

//输入流
private Bitmap getBitmapFromStream(InputStream inputStream) {
      return BitmapFactory.decodeStream(inputStream);
}

4.取得Bitmap的相關資訊:

這個,只要我們取得了Bitmap對象,就可以呼叫相關方法來取得對應的參數了,getByteCount獲得大小, getHeight和getWidth這些~這裡就不寫了,自己查文檔!


5.摳圖片上的某一角下來

有時,可能你想把圖片上的某一角扣下來,直接透過Bitmap的createBitmap()扣下來即可 參數依序為:處理的bitmap對象,起始x,y座標,以及截取的寬高

Bitmap bitmap1 = BitmapFactory.decodeResource(getResources(), R.mipmap.pic_meizi);
Bitmap bitmap2 = Bitmap.createBitmap(bitmap1,100,100,200,200);
img_bg = (ImageView) findViewById(R.id.img_bg);
img_bg.setImageBitmap(bitmap2);

運行效果圖

原圖:

5.png

切下來的一角:

6.png


#6.對Bitmap進行縮放

7.png

我們這裡不用Matrix來對Bitmap,而是直接使用Bitmap給我們提供的createScaledBitmap來實現, 參數依序是:處理的bitmap對象,縮放後的寬高,


7.使用Bitmap進行截圖

運行效果圖

8.gif

####################### ##############實作程式碼###:###
public class MainActivity extends AppCompatActivity {
    static ByteArrayOutputStream byteOut = null;
    private Bitmap bitmap = null;
    private Button btn_cut;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btn_cut = (Button) findViewById(R.id.btn_cut);
        btn_cut.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                captureScreen();
            }
        });
    }

    public void captureScreen() {
        Runnable action = new Runnable() {
            @Override
            public void run() {
                final View contentView = getWindow().getDecorView();
                try{
                    Log.e("HEHE",contentView.getHeight()+":"+contentView.getWidth());
                    bitmap = Bitmap.createBitmap(contentView.getWidth(),
                            contentView.getHeight(), Bitmap.Config.ARGB_4444);
                    contentView.draw(new Canvas(bitmap));
                    ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
                    bitmap.compress(Bitmap.CompressFormat.JPEG, 100, byteOut);
                    savePic(bitmap, "sdcard/short.png");
                }catch (Exception e){e.printStackTrace();}
                finally {
                    try{
                        if (null != byteOut)
                            byteOut.close();
                        if (null != bitmap && !bitmap.isRecycled()) {
//                            bitmap.recycle();
                            bitmap = null;
                        }
                    }catch (IOException e){e.printStackTrace();}

                }
            }
        };
        try {
            action.run();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void savePic(Bitmap b, String strFileName) {
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(strFileName);
            if (null != fos) {
                boolean success= b.compress(Bitmap.CompressFormat.PNG, 100, fos);
                fos.flush();
                fos.close();
                if(success)
                    Toast.makeText(MainActivity.this, "截屏成功", Toast.LENGTH_SHORT).show();
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
#######程式碼分析###:###

程式碼非常簡單,final View contentView = getWindow().getDecorView();這句程式碼是取得目前XML 根節點的View!然後設定截圖的大小,呼叫下contentView.draw(new Canvas(bitmap));好了,然後 bitmap轉換成流,接著寫入SD卡,沒了~當然從結果我們也可以看出,截圖截取的是改APP的內容而已! 如果要截全屏,自行谷歌~!


本節小結:

本節跟大家講解下Bitmap,BitmapFactory和他的靜態內部類Options ,以及BitmapDrawable的 基本使用,其實Bitmap我們知道怎麼創建就好了,他的擴充一般是透過Matrix和Canvas來實現的, Bitmap,我們更多的時候關注的是OOM問題,下一節我們就來學習下如何避免Bitmap的OOM問題! 謝謝~9.gif


#