Android 自定義Camera相機(封裝工具類賊簡單)
阿新 • • 發佈:2019-02-02
背景
目前公司的專案都是,針對生物認證來進行一些驗證之類的功能,比方說,互動式活體檢測,人臉1v1(對比),人臉1vN(搜尋)。用系統自帶的相機?當然是不夠用了,不夠用那就自定義啦,就像是前幾天七夕,沒物件怎麼辦,當然是new一個了。對了結尾還提供了一個呼叫極其簡單的相機工具類。
瞭解一下
通常我們呼叫相機的話,無非就是Intent開啟系統自帶的相機,要自定義相機的話,也要通過Android框架所提供的API讓我們控制相機硬體,來達到我們的需求。
使用API來控制相機我們需要用到關鍵類和介面:
- 使用Camera物件來控制相機
- 使用SurfaceView來展現照相機採集的影象
- 通過surfaceholder來控制surfac的尺寸和格式,修改surface的畫素,監視surface的變化等等
- 通過SurfaceHolder.Callback 介面,監聽surface狀態變化
Camera類
//判斷是否存在相機硬體
FEATURE_CAMERA : 後置相機
FEATURE_CAMERA_FRONT : 前置相機
PackageManager pm = getPackageManager();
boolean hasACamera = pm.hasSystemFeature(PackageManager.FEATURE_CAMERA);
//開啟相機
camera = Camera.open();//預設開啟後置相機
camera = Camera.open(0);//也可傳遞引數開啟指定相機 0為後置 1為前置
//設定相機引數
Camera.Parameters parameters = camera.getParameters();//得到攝像頭的引數
parameters.setJpegQuality(100);//設定照片的質量
parameters.setPreviewSize(640,480);//設定預覽尺寸
parameters.setPictureSize(640,480);//設定照片尺寸
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);// 連續對焦模式
//Camera.Parameters.FOCUS_MODE_AUTO; //自動聚焦模式
//Camera.Parameters.FOCUS_MODE_INFINITY;//無窮遠
//Camera.Parameters.FOCUS_MODE_MACRO;//微距
//Camera.Parameters.FOCUS_MODE_FIXED;//固定焦距
camera.setParameters(parameters);
//設定相機旋轉角度
//有些手機上面會出現旋轉90度的情況(Android相容性問題)所以我們要在這裡適配一下
Camera.CameraInfo info = new Camera.CameraInfo();
//獲取攝像頭資訊
Camera.getCameraInfo(cameraId, info);
int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
//獲取攝像頭當前的角度
int degrees = 0;
switch (rotation) {
case Surface.ROTATION_0:
degrees = 0;
break;
case Surface.ROTATION_90:
degrees = 90;
break;
case Surface.ROTATION_180:
degrees = 180;
break;
case Surface.ROTATION_270:
degrees = 270;
break;
}
int result;
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
// 前置攝像頭
result = (info.orientation + degrees) % 360;
result = (360 - result) % 360;
} else {
// 後置攝像頭
result = (info.orientation - degrees + 360) % 360;
}
camera.setDisplayOrientation(result);
//開始預覽以及結束預覽
camera.setPreviewDisplay(surfaceHolder);//通過SurfaceView顯示取景畫面
camera.startPreview();//開始預覽
camera.stopPreview();//結束預覽
camera.release();
//拍照
camera.takePicture(null, null, new Camera.PictureCallback() {
@Override
public void onPictureTaken(byte[] data, Camera camera) {
//拍照 byte資料即是照取的圖片 可以直接轉換成bitmap
//此方法只會呼叫一次 建議寫在點選事件中
Bitmap mbitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
}
});
//獲取實時的照相機資料
camera.setPreviewCallback(new Camera.PreviewCallback() {
@Override
public void onPreviewFrame(byte[] data, Camera camera) {
//這裡byte資料即是實時獲取的幀資料 只要相機正在預覽就會一直回撥此方法
//需要注意的是 這裡的byte資料不能夠直接使用 需要轉換下格式
Bitmap bmp = null;
try {
YuvImage image = new YuvImage(bytes, ImageFormat.NV21, 640, 480, null);
if (image != null) {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
image.compressToJpeg(new Rect(0, 0, 640, 480), 80, stream);
bmp = BitmapFactory.decodeByteArray(stream.toByteArray(), 0, stream.size());
stream.close();
}
} catch (Exception ex) {
Log.e("Sys", "Error:" + ex.getMessage());
}
}
});
SurfaceView 控制元件
//獲取SurfaceHolder物件
SurfaceHolder mSurfaceHolder = mSurfaceView.getHolder();
// 設定 Surface 格式
// 引數: PixelFormat中定義的 int 值 ,詳細參見 PixelFormat.java
mSurfaceHolder.setFormat(PixelFormat.TRANSPARENT);
//保持螢幕常亮(可選)
mSurfaceHolder.setKeepScreenOn(true);
// 新增 Surface 的 callback 介面
mSurfaceHolder.addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
//在 Surface 首次建立時被立即呼叫:獲得焦點時。一般在這裡開啟相機預覽
}
@Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int format, int width, int height) {
//在 Surface 格式 和 大小發生變化時會立即呼叫,可以在這個方法中更新 Surface
}
@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
//在 Surface 被銷燬時立即呼叫:失去焦點時。一般在這裡將相機預覽停止銷燬
}
});
完整的CameraActivity
public class CameraActivity extends BaseActivity{
private Camera camera;
private SurfaceView mSurfaceView;
private boolean isPreview = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_camera);
mSurfaceView = findViewById(R.id.sv);
// 獲得 SurfaceHolder 物件
SurfaceHolder mSurfaceHolder = mSurfaceView.getHolder();
// 設定 Surface 格式
// 引數: PixelFormat中定義的 int 值 ,詳細參見 PixelFormat.java
mSurfaceHolder.setFormat(PixelFormat.TRANSPARENT);
// 如果需要,保持螢幕常亮
mSurfaceHolder.setKeepScreenOn(true);
// 新增 Surface 的 callback 介面
mSurfaceHolder.addCallback(mSurfaceCallback);
findViewById(R.id.btn_camera).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
camera.takePicture(null, null, new Camera.PictureCallback() {
@Override
public void onPictureTaken(byte[] data, Camera camera) {
//進行拍照操作
}
});
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
if(camera != null) {
camera.release();
}
finish();
}
private SurfaceHolder.Callback mSurfaceCallback = new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
try {
// Camera,open() 預設返回的後置攝像頭資訊
camera = Camera.open();
//此處也可以設定攝像頭引數
Camera.Parameters parameters = camera.getParameters();//得到攝像頭的引數
parameters.setJpegQuality(100);//設定照片的質量
parameters.setPreviewSize(1920,1080);//設定預覽尺寸
parameters.setPictureSize(1920,1080);//設定照片尺寸
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);// 連續對焦模式
camera.setParameters(parameters);
//設定角度,此處 CameraId 我預設 為 0 (後置)
// CameraId 也可以 通過 參考 Camera.open() 原始碼 方法獲取
setCameraDisplayOrientation(CameraActivity.this,0,camera);
camera.setPreviewDisplay(surfaceHolder);//通過SurfaceView顯示取景畫面
camera.startPreview();//開始預覽
isPreview = true;//設定是否預覽
} catch (IOException e) {
Log.e("surfaceCreated", e.toString());
}
}
@Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
if(camera != null){
if(isPreview){//正在預覽
camera.stopPreview();
camera.release();
}
}
}
};
/**
* 設定 攝像頭的角度
* @param activity 上下文
* @param cameraId 攝像頭ID(假如手機有N個攝像頭,cameraId 的值 就是 0 ~ N-1)
* @param camera 攝像頭物件
*/
public static void setCameraDisplayOrientation(Activity activity, int cameraId, Camera camera) {
Camera.CameraInfo info = new Camera.CameraInfo();
//獲取攝像頭資訊
Camera.getCameraInfo(cameraId, info);
int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
//獲取攝像頭當前的角度
int degrees = 0;
switch (rotation) {
case Surface.ROTATION_0:
degrees = 0;
break;
case Surface.ROTATION_90:
degrees = 90;
break;
case Surface.ROTATION_180:
degrees = 180;
break;
case Surface.ROTATION_270:
degrees = 270;
break;
}
int result;
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
// 前置攝像頭
result = (info.orientation + degrees) % 360;
result = (360 - result) % 360; // compensate the mirror
} else {
// 後置攝像頭
result = (info.orientation - degrees + 360) % 360;
}
camera.setDisplayOrientation(result);
}
}
別忘了新增許可權
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" android:required="true" />
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
極其簡單的相機工具類
//首先在佈局中註冊控制元件
<com.xzq.camera.CameraPreview
android:id="@+id/cp"
android:layout_width="match_parent"
android:layout_height="match_parent" />
//然後在你想開啟相機的地方
//找到控制元件
CameraPreview cp = findViewById(R.id.cp);
CameraUtil.INSTANCE.setPreviewView(cp);//設定預覽控制元件
//設定預覽監聽
CameraUtil.INSTANCE.setOnCameraListener(new OnCameraListener() {
@Override
public void onCameraDataFetched(byte[] bytes) {
//實時獲取相機資料
}
@Override
public void onError(CameraError cameraError) {
//開啟相機錯誤
}
});
是不是極其簡單,跟上邊的一比,我都不明白我為什麼要寫上邊的東西。。。。。
相機jar包的下載地址:https://download.csdn.net/download/qq_38001118/10615455
如果有什麼擴充套件的需求或者我有什麼遺失和錯誤歡迎評論,我會第一時間回覆和更新。