一、前言
最近前同事兼好基友老戴問我要我之前那個可以無卡頓拍照的demo,翻了一翻我的demo項目文件夾,有點真實

加上程序員都是不喜歡看自己以前寫的代碼的特性,于是決定將這個功能封裝一下,方便他人當然也是方便自己
這個功能的出處還是以前我們做的刷卡考勤機,考勤的時候需要取到考勤圖片,所以需要進行拍照
我一開始只是使用常規(guī)的Camera的takePicture方法來獲取照片,但是實際應用中會出現(xiàn),拍照速度緩慢
當時我還去現(xiàn)場看了一下使用情況,負責人跟我抱怨說拍攝速度很慢,給我演示了一下,確實是有一個卡頓,當然這很好理解,我理直氣壯的跟她解釋說你用手機拍照不也是會停頓一下的嗎,手機需要聚焦啊,這個本來就是這樣的
而負責人跟我說了某某考勤機拍照沒有停頓啊,非??斓?,我第一反應是,應該是windows的機子
結果看到發(fā)現(xiàn)人家的也是android的機器,于是便陷入了沉思
我們別的我不知道,但是抄襲這一招可是鐵打的,于是乎便開始了對android相機的探索
正如我標題寫的,為了實現(xiàn)我卡頓的拍照,使用的是SurfaceView+Camera的方式,通過相機的預覽到surfaceView上,然后通過Camera的setPreviewCallback函數(shù)的回調來當前幀的圖片,便不會有任何的卡頓
二、效果圖
點擊拍照之后,可以獲取到當前幀的圖片的BitMap對象,以及保存至本地的路徑

三、功能實現(xiàn)
(一)如何使用
首先先看一下布局文件
一個SurfaceView用來實時顯示相機的畫面
文本框和ImageView用來顯示保存圖片的路徑和顯示圖片
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:orientation="vertical"
android:layout_height="match_parent"
tools:context=".MainActivity">
<SurfaceView
android:id="@+id/surfaceview"
android:layout_width="320dp"
android:layout_height="240dp" />
<LinearLayout
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<Button
android:id="@+id/btn_take_photo"
android:text="拍照"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/tv_pic_dir"
android:text="圖片路徑:"
android:textSize="14sp"
android:textColor="#000000"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<ImageView
android:background="#000000"
android:id="@+id/img_pic"
android:layout_width="160dp"
android:layout_height="120dp" />
</LinearLayout>
</LinearLayout>
使用已封裝的CameraTakeManager,傳入三個參數(shù)分別為activity對象,surfaceView控件對象,一個自定義的回調
回調的兩個函數(shù)onSuccess中返回以保存的圖片和BitMap對象
onFail返回失敗信息
manager = new CameraTakeManager(this, previewView, new CameraTakeListener() {
@Override
public void onSuccess(File bitmapFile, Bitmap mBitmap) {
imgPic.setImageBitmap(mBitmap);
tvPicDir.setText("圖片路徑:" + bitmapFile.getPath());
}
@Override
public void onFail(String error) {
LogUtil.e(error);
}
});
通過點擊按鈕用來獲取照片,進入CameraTakeManager的回調
@OnClick({R.id.btn_take_photo})
public void onClick(View view) {
switch (view.getId()) {
case R.id.btn_take_photo:
/** 點擊拍照獲取照片*/
manager.takePhoto();
break;
}
}
(二)實現(xiàn)的代碼
這邊自定義了一個SurfaceViewCallback類來實現(xiàn)SurfaceHolder.Callback接口
先是在surfaceChanged回調中開啟camera的預覽
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
if (previewing) {
mCamera.stopPreview();
previewing = false;
}
try {
mCamera.setPreviewDisplay(holder);
mCamera.startPreview();
previewing = true;
setCameraDisplayOrientation(activity, mCurrentCamIndex, mCamera);
} catch (Exception e) {
}
}
在surfaceCreated回調中實現(xiàn)Camera的setPreviewCallback函數(shù)來獲取相機每一幀的回調
用canTake變量來判斷當前是否需要拍照,為true時,則取當前幀的圖像,生成bitmap同時壓縮一份圖片文件到本地保存,并把數(shù)據(jù)回調給接口
實現(xiàn)拍照功能
@Override
public void surfaceCreated(SurfaceHolder holder) {
if (!hasSurface) {
hasSurface = true;
mCamera = openFrontFacingCameraGingerbread();
if (mCamera == null){
listener.onFail("沒有可用的攝像頭");
return;
}
mCamera.setPreviewCallback(new Camera.PreviewCallback() {
@Override
public void onPreviewFrame(byte[] bytes, Camera camera) {
if (canTake) {
getSurfacePic(bytes, camera);
canTake = false;
}
}
});
}
}
四、結語
到這里就算是完成了,技藝不精,希望大家多提提意見,我也會第一時間改良,記得給我點贊哦
在最后貼一下github的源碼地址https://github.com/Giftedcat/CameraTakeManager