【笔记】Android5.0前后的Camera和Camera2 预览、拍照、摄像头切换

上篇在做相机桌面的时候用的是5.0之前的Camera,但Camera在5.0之后已经被淘汰了,官方也是推荐使用Camera2,这两天也分别尝试了下Camera和Camera2的使用。

因为新的API换了加购,让开发者用起来更麻烦了。虽然按照官网的说法可能逻辑更加清晰,理解起来也更容易懂了,但是个人使用后的感觉还是觉得太麻烦了。如果仅仅只是拍个照预览下什么的还是乖乖使用Camera吧,用Camera2实在太麻烦。

android6.0之后要动态请求权限,相机使用需要相机权限,拍照保存图片需要文件读写权限。Android6.0之后权限请求工具类

Camera使用

1、打开相机

可传入cameraId来打开前置或后置摄像头

Camera.open()

2、设置预览并取景

用SurfaceView显示预览,由于相机方向是横屏的,需要旋转90度

    try {
        camera.setDisplayOrientation(90);
        camera.setPreviewDisplay(surfaceHolder);
        camera.startPreview();
    } catch (IOException e) {
        e.printStackTrace();
    }

3、拍照

takePicture第三个参数回调方法是拍照传回来的二进制数据,可以进行图片保存,拍完照之后需要再次打开预览,不然就定格在拍照的画面

    camera.takePicture(new Camera.ShutterCallback() {
        @Override
        public void onShutter() {
            //按下快门
        }
    }, new Camera.PictureCallback() {
        @Override
        public void onPictureTaken(byte[] data, Camera camera) {
            
        }
    }, new Camera.PictureCallback() {
        @Override
        public void onPictureTaken(byte[] data, Camera camera) {
            savePicture(data);
            startPreview(camera, surfaceHolder);
        }
    });

4、切换摄像头

遍历所有摄像头,前置后置切换,切换前停止预览,再打开相应摄像头

    /**
     * 切换摄像头
     */
    private void switchCamera() {
        int count = Camera.getNumberOfCameras();
        Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
        for (int i = 0; i < count; i++) {
            Camera.getCameraInfo(i, cameraInfo);
            if (currentCameraId == Camera.CameraInfo.CAMERA_FACING_BACK && cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
                //后置变前置
                stopPreview();
                currentCameraId = Camera.CameraInfo.CAMERA_FACING_FRONT;
                openCamera();
                break;
            } else if (currentCameraId == Camera.CameraInfo.CAMERA_FACING_FRONT && cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
                //前置变后置
                stopPreview();
                currentCameraId = Camera.CameraInfo.CAMERA_FACING_BACK;
                openCamera();
                break;
            }
        }
    }

Camera2使用

Camera2用管道的概念将安卓设备和摄像头之间连通,应用向设备发送请求,设备返回结果,再由应用处理结果。

1、CameraManager

CameraManager是所有CameraDevice的管理者。

    CameraManager cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);

2、打开摄像头

    //第一个参数指定哪个摄像头,第二个参数打开摄像头的状态回调,第三个参数是运行在哪个线程(null是当前线程)
    cameraManager.openCamera(String.valueOf(currentCameraId), stateCallback, handler);
打开相机的回调

    //打开相机回调
    private CameraDevice.StateCallback stateCallback = new CameraDevice.StateCallback() {
        @Override
        public void onOpened(@NonNull CameraDevice camera) {
            Log.i(TAG, "onOpened");
            //相机开启,打开预览
            cameraDevice = camera;
            startPreview();
        }

        @Override
        public void onDisconnected(@NonNull CameraDevice camera) {
            Log.i(TAG, "onDisconnected");
            //相机关闭
            camera.close();
            cameraDevice = null;
        }

        @Override
        public void onError(@NonNull CameraDevice camera, int error) {
            Log.i(TAG, "onError");
            //相机报错
            camera.close();
            cameraDevice = null;
            finish();
        }
    };

3、预览

相机打开后创建CameraBuilder,再创建CameraCaptireSession,最后调用CameraCaptureSession#setRepeatingRequest预览

    /**
     * 开始预览
     */
    private void startPreview() {
        try {
            previewBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
            //设置预览数据输出界面
            previewBuilder.addTarget(surfaceHolder.getSurface());
            //创建相机捕获会话,
            //第一个参数是捕获数据的输出Surface列表,第二个参数是CameraCaptureSession状态回调接口,第三个参数是在哪个线程(null是当前线程)
            cameraDevice.createCaptureSession(Arrays.asList(surfaceHolder.getSurface(), imageReader.getSurface()), sessionStateCallback, handler);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }
CameraCaptureSession会话

    //创建session回调
    private CameraCaptureSession.StateCallback sessionStateCallback = new CameraCaptureSession.StateCallback() {
        @Override
        public void onConfigured(@NonNull CameraCaptureSession session) {
            try {
                cameraCaptureSession = session;
                //自动对焦
                previewBuilder.set(CaptureRequest.CONTROL_AF_MODE, CameraMetadata.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
                cameraCaptureSession.setRepeatingRequest(previewBuilder.build(), null, handler);
            } catch (CameraAccessException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onConfigureFailed(@NonNull CameraCaptureSession session) {
            session.close();
            cameraCaptureSession = null;
            cameraDevice.close();
            cameraDevice = null;
        }
    };

4、拍照

    /**
     * 拍照
     */
    private void takePhoto() {
        try {
            captureBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
            captureBuilder.addTarget(imageReader.getSurface());
            //自动对焦
            captureBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
            cameraCaptureSession.stopRepeating();
            //拍照
            cameraCaptureSession.capture(captureBuilder.build(), captureCallback, handler);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }
拍完照后回调,重新打开预览

    //拍完照回调
    private CameraCaptureSession.CaptureCallback captureCallback = new CameraCaptureSession.CaptureCallback() {
        @Override
        public void onCaptureProgressed(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull CaptureResult partialResult) {
            super.onCaptureProgressed(session, request, partialResult);
        }

        @Override
        public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) {
            super.onCaptureCompleted(session, request, result);
            try {
                //自动对焦
                captureBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_CANCEL);
                //重新打开预览
                session.setRepeatingRequest(previewBuilder.build(), null, handler);
            } catch (CameraAccessException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onCaptureFailed(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull CaptureFailure failure) {
            super.onCaptureFailed(session, request, failure);
            cameraCaptureSession.close();
            cameraCaptureSession = null;
            cameraDevice.close();
            cameraDevice = null;
        }
    };
拍照处理器

    /**
     * 初始化拍照处理器
     */
    private void initImageReader() {
        imageReader = ImageReader.newInstance(previewSize.getWidth(), previewSize.getHeight(), ImageFormat.JPEG, 1);
        //监听ImageReader时间,有图像数据可用时回调,参数就是帧数据
        imageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {
            @Override
            public void onImageAvailable(ImageReader reader) {
                Image image = reader.acquireLatestImage();
                //将帧数据转换成字节数组
                ByteBuffer byteBuffer = image.getPlanes()[0].getBuffer();
                byte[] data = new byte[byteBuffer.remaining()];
                byteBuffer.get(data);
                //保存照片
                savePicture(data);
                image.close();
            }
        }, handler);
    }

5、切换摄像头

遍历所有cameraId,前置后置互相切换,切换前先关闭CameraDevice,再打开对应摄像头

    /**
     * 切换摄像头
     */
    private void switchCamera() {
        try {
            for (String cameraId : cameraManager.getCameraIdList()) {
                CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(cameraId);
                StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
                Size maxSize = getMaxSize(map.getOutputSizes(SurfaceHolder.class));
                if (currentCameraId == CameraCharacteristics.LENS_FACING_BACK && characteristics.get(CameraCharacteristics.LENS_FACING) == CameraCharacteristics.LENS_FACING_FRONT) {
                    //前置转后置
                    previewSize = maxSize;
                    currentCameraId = CameraCharacteristics.LENS_FACING_FRONT;
                    cameraDevice.close();
                    openCamera();
                    break;
                } else if (currentCameraId == CameraCharacteristics.LENS_FACING_FRONT && characteristics.get(CameraCharacteristics.LENS_FACING) == CameraCharacteristics.LENS_FACING_BACK) {
                    //后置转前置
                    previewSize = maxSize;
                    currentCameraId = CameraCharacteristics.LENS_FACING_BACK;
                    cameraDevice.close();
                    openCamera();
                    break;
                }
            }
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

权限

    <uses-permission android:name="android.permission.CAMERA" />

    <uses-feature android:name="android.hardware.camera" />
    <uses-feature android:name="android.hardware.camera.autofocus" />


简单使用Demo












猜你喜欢

转载自blog.csdn.net/q1113225201/article/details/77688248