1. 程式人生 > >Android 自定義Camera相機(封裝工具類賊簡單)

Android 自定義Camera相機(封裝工具類賊簡單)

背景

目前公司的專案都是,針對生物認證來進行一些驗證之類的功能,比方說,互動式活體檢測,人臉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
如果有什麼擴充套件的需求或者我有什麼遺失和錯誤歡迎評論,我會第一時間回覆和更新。