位圖:Bitmap
在上一節學習 Drawable 圖像資源的時候我們在很多地方用到了 bitmap,bitmap 其實就是真實圖片在 Android 中最直接的表現形式,這一節我們來仔細學習一下 Bitmap 的使用。
1. 什么是 Bitmap
Bitmap 在 Android 中對應一張圖片文件,它是一個二位系統,通過編碼記錄了一張圖片的完整形式。以左上角為原點,向右和向下建立一個(X , Y)坐標系,坐標系中的每一個點都成為一個“像素”。在不同編碼格式的 Bitmap 里一個像素占的 bit 數有所不同,這些 bit 共同表征了當前像素的色值,可能是8 bit、16 bit 或者 24 bit 等等,最后將這些所有的色值組合起來就成了一張完整的原始圖片。
我們在 Android 中繪制的一切圖像都是一個 Bitmap,我們可以創建一個 Bitmap 示例或者使用 Bitamp 工具來修改、優化一個圖像資源。
2. Bitmap 的常用 API
- createBitmap(int width, int height, Bitmap.Config config):
根據傳入的寬高、創建一個可修改的 Bitmap 對象 - createBitmap(DisplayMetrics display, int width, int height, Bitmap.Config config):
相比上一個接口,這個可以在創建的時候傳入一些參數 - createBitmap(Bitmap src):
根據傳入的 Bitamp 創建一個新的 Bitamp - copy(Bitmap.Config config, boolean isMutable):
將 Bitamp 對象的所有像素復制到一個新的 Bitmap 當中 - extractAlpha():
提取原始 Bitmap 的透明度并返回一個新的 Bitamp - getConfig():
獲取 Bitamp 的配置信息 - getDensity():
返回 Bitamp 的圖片像素密度 - getRowBytes():
返回 Bitamp 圖片的像素字節數組 - setPixel(int x, int y, int color):
設置圖片的(x, y)坐標點上的色值 - setDensity(int density):
設置圖片像素密度
3. 獲取 Bitamp 實例
Android 提供了多種方法獲取 Bitamp 實例,我們可以直接從 ImageView 上拿到當前設置的圖片的 Bitamp 對象:
private Bitmap bmp;
private ImageView img;
img = (ImageView)findViewById(R.id.imageView);
BitmapDrawable drawable = (BitmapDrawable)img.getDrawable();
bmp = drawable.getBitmap();
從上面的代碼中也可以看到我們可以通過 Drawable 的getBitmap()
方法從 Drawable 對象中提取出 Bitamp。
Android 還提供了一個 BitampFractory 工廠對象,專門讓我們去獲取 Bitmap 實例,主要有以下幾種常用方法:
// 從資源文件中解碼出 Bitmap
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 使用示例
通過這兩節的學習,我們知道 Drawable 和 Bitmap 可以相互轉換,接下來編寫一個示例完成將 ImageView 中的 Drawable 對象保存到本地的示例程序。
4.1 MainActivity 截圖功能
package com.emercy.myapplication;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class MainActivity extends Activity {
static ByteArrayOutputStream byteOut = null;
private Bitmap bitmap = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button btn_cut = (Button) findViewById(R.id.button);
btn_cut.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
capture();
}
});
}
public void capture() {
Runnable action = new Runnable() {
@Override
public void run() {
final View contentView = getWindow().getDecorView();
try {
bitmap = Bitmap.createBitmap(contentView.getWidth(),
contentView.getHeight(), Bitmap.Config.ALPHA_8);
contentView.draw(new Canvas(bitmap));
ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, byteOut);
save(bitmap);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (null != byteOut)
byteOut.close();
if (null != bitmap && !bitmap.isRecycled()) {
bitmap = null;
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
};
try {
action.run();
} catch (Exception e) {
e.printStackTrace();
}
}
private void save(Bitmap b) {
FileOutputStream fos;
try {
fos = new FileOutputStream("sdcard/short.png");
boolean success = b.compress(Bitmap.CompressFormat.PNG, 80, fos);
fos.flush();
fos.close();
if (success) {
Toast.makeText(MainActivity.this, "截圖完成", Toast.LENGTH_SHORT).show();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
在capture()
方法中,我們獲取當前 Activity 的“DecorView”(Activtiy 的頂層 View,我們設置的 CotentView 是其子 View),然后獲取 DecorView 的輸入流并會轉化成 Bitmap,最后直接輸出到文件中即可。
4.2 布局文件
這一節的布局文件可以任意,你可以嘗試寫出各種復雜的 UI 樣式,因為上面的capture()
方法是從“DecorView”中獲取 Bitmap,所以無論你寫了多少 View,最終都會通過setContentView()
設置到“ContentView”中,而 ContentView 也是在 DecorView 中的,所以無論怎么寫都逃離不開截圖范圍,最后記得加一個 Button 用于觸發截屏。
5. 小結
本節進一步講述了 Andorid 圖像相關的內容,相比 Drawable,Bitmap 直接對應一張圖片,更加具體。它可以與 Drawable 相互轉化,并提供了多種 API 來直接操作一張圖片,在做圖片裁剪和修改的場景非常適用。