Android Camer2与Camera1 自定义相机拍照封装实例讲解

版权声明:本文为博主原创文章,转载请注明出处。 https://blog.csdn.net/Android_Technology/article/details/81538259

引言


我们在Android开发中肯定都免不了要使用到相机拍照功能,一般正常情况下使用Android自带相机就能满足很大一部分需求了。不过某些情况下我们还是需要用到自定义的相机功能。而笔者所在公司恰好又有一个需求就是要针对不同厂商的定制的TV盒子做人脸拍照识别功能,而摄像头是外置摄像头,电视盒子因为定制化的原因调用原生相机总是会有各种的问题,因此就不得不区分使用Camera1和Camera2 API来实现相机拍照的功能。 这篇文章则是笔者根据自己的经验将Camer1和Camera2分别封装到不同的View中来实现相机的适配功能。

(注:Camera1在使用中的源码为android.hardware.Camera,这里方便做区分统一都写为Camer1)


下面是项目的源码地址,小伙伴们可根据需要自行取用:
Android Camer2与Camera1 自定义相机拍照封装实例


正文

首先Camera1应该是我们刚开始接触Andorid 自定义相机的时候就会了解到的API了,因为Andorid系统的升级更新,从Android 5.0之后官方就基本推荐使用Camera2 API来实现自定义相机。关于Camera2与Camera1相对比的具体优势和实现细节,基本都在从我之前写的这篇文章中都有解答: Android Camera2相机使用流程讲解
这里我就直接开始代码封装的解读了,首先我们知道,使用Camera2 API必须是在Android 5.0之后,同时设备必须支持Camera2。所以一般情况下我们需要在针对使用Camera1还是使用Camera2需要做一个判断:只有Android 5.0及其以上设备,同时能够支持Camera2的设备。我们才可使用Cmaera2 API来实现自定义相机,否则就使用Camera1 API。



首先我做了一个CameraView通用接口,定义好相机使用过程中需要的通用的方法(拍照,切换相机等)。这样可以通过这个通用接口实现面向接口编程啦。

public interface CameraView {

    /**
     * 与生命周期onResume调用
     */
    void onResume();

    /**
     * 与生命周期onPause调用
     */
    void onPause();

    /**
     * 拍照
     */
    void takePicture();

    /**
     * 拍照(有回调)
     */
    void takePicture(TakePictureCallback takePictureCallback);

    /**
     * 设置保存的图片文件
     *
     * @param pictureSavePath 拍摄的图片返回的绝对路径
     */
    void setPictureSavePath(String pictureSavePath);

    /**
     * 切换相机摄像头
     */
    void switchCameraFacing();


    interface TakePictureCallback {

        void success(String picturePath);

        void error(final String error);
    }

}

接口中定义了onResume,onPause方法 因为相机需要通过这两个方法去判断启用还是暂停,通过Activity的回调的这两个生命周期。我们能够很好的去处理相机逻辑。其他的几个方法就是一般相机通用的方法啦。这里说一下拍照方法我加入了有回调和没回调方法,这个也是考虑到了业务逻辑的原因。
比如有时候我们只是想要调用拍照接口,然后让相机拍照后自动保存图片到文件即可,至于是否成功我们都可以不用考虑就调用无回调接口即可。
然后就是setPictureSavePath,设备保存的图片文件接口。通过此接口可以将相机拍照图片保存到我们指定的路径下,如果不设置这里我会保存在一个默认的路径下。

下面的代码分别就是对 Camera1 API 和 Camera2 API 实现的自定义View的核心代码部分了。
源码直接贴出来啦,其中对应API的解释也有,小伙伴们可以直接根据通过撸代码了解对应API啦。

Camera1 API的封装及其实现

Camera1 的显示View我是用到的SurfaceView。用过自定义相机的小伙伴应该对这个很熟悉了,另外有关Camera1的API网上也有很多。
我这里主要重点提一下使用相机的时候判断相机的旋转方向的方法,选择最佳的相机预览大小和最佳的拍照图片大小。
关于相机预览旋转方向,我这里用到了google官方推荐的方式,传入的参数CameraId就是相机面对方向(前置方向或后置方向)。对相机预览方向还有疑问的小伙伴可以看这里哦: Android相机预览方向深入探究
在这里插入图片描述

另外一点就是关于选择相机最佳预览方向,我这里也是使用了谷歌官方推荐的方案。
大致实现流程就是通过 Camerad的Parameters.getSupportedPreviewSizes() 获取相机预览支持的大小列表,通过Camerad的Parameters.getSupportedPictureSizes() 获取相机照片支持的大小列表。然后再根据我们相机的长宽比(比如4:3还是16:9),筛选出最大的符合长宽比的尺寸列表,然后再中这些符合条件的尺寸列表中选出最大的即可。

具体实现可下面的参考源码



public class Camera1Preview extends SurfaceView implements CameraView, SurfaceHolder.Callback {

    private static final String TAG = "Camera1Preview";

    private Camera mCamera;
    private Context mContext;
    private int mCameraCount;
    private int mCurrentCameraFacing = Camera.CameraInfo.CAMERA_FACING_BACK;

    private SurfaceHolder mSurfaceHolder;

    /**
     * 标识相机是否正在拍照过程中
     */
    private final AtomicBoolean isPictureCaptureInProgress = new AtomicBoolean(false);


    private int mRatioWidth = 0;
    private int mRatioHeight = 0;

    public Camera1Preview(Context context) {
        this(context, null);
    }

    public Camera1Preview(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public Camera1Preview(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mContext = context;
        initCamera();
    }


    private void initCamera() {
        mCamera = getCameraInstance();
        if (mCamera == null)
            return;

        //得到摄像头数量
        mCameraCount = Camera.getNumberOfCameras();
        mSurfaceHolder = getHolder();
        // 设置SurfaceHolder.Callback回调,这样我们可以在创建或销毁Surface时处理相应的逻辑
        mSurfaceHolder.addCallback(this);
        //设置屏幕常亮
        mSurfaceHolder.setKeepScreenOn(true);
        //点击自动对焦
        setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                if (mCamera != null) {
                    mCamera.autoFocus(null);
                }
            }
        });
    }


    @Override
    public void onResume() {
        if (mCamera == null)
            mCamera = getCameraInstance();
    }


    @Override
    public void onPause() {
        releaseCamera();
    }

    /**
     * 拍照方法(无回调,默认保存到文件中)
     */
    @Override
    public void takePicture() {
        if (mCamera == null) {
            throw new IllegalStateException(
                    "Camera is not ready. Call start() before takePicture().");
        }
        takePictureInternal();
    }

    /**
     * 拍照方法(有回调)
     *
     * @param callback
     */
    @Override
    public void takePicture(TakePictureCallback callback) {
        if (mCamera == null) {
            throw new IllegalStateException(
                    "Camera is not ready. Call start() before takePicture().");
        }
        takePictureCallback = callback;
        takePictureInternal();
    }

    private void takePictureInternal() {
        //如果正在拍照处理中,则不能调用takePicture方法,否则应用会崩溃
        if (!isPictureCaptureInProgress.get()) {
            isPictureCaptureInProgress.set(true);
            mCamera.takePicture(null, null, mPictureCallback);
        }
    }


    /**
     * 设置图片的保存路径
     *
     * @param pictureSavePath
     */
    @Override
    public void setPictureSavePath(String pictureSavePath) {
        mPictureSaveDir = pictureSavePath;
    }

    /***
     * 切换相机摄像头
     */
    @Override
    public void switchCameraFacing() {
        if (mCameraCount > 1) {
            mCurrentCameraFacing = (mCurrentCameraFacing == Camera.CameraInfo.CAMERA_FACING_BACK) ?
                    Camera.CameraInfo.CAMERA_FACING_FRONT : Camera.CameraInfo.CAMERA_FACING_BACK;
            releaseCamera();
            startPreview(mSurfaceHolder);
        } else {
            //手机不支持前置摄像头
        }
    }


    /**
     * 设置此视图的宽高比。
     * 视图的大小将基于从参数计算的比率来测量。
     * 请注意,参数的实际大小并不重要,也就是说,setAspectRatio(2, 3)setAspectRatio(4, 6)会得到相同的结果。
     *
     * @param width  Relative horizontal size
     * @param height Relative vertical size
     */
    public void setAspectRatio(int width, int height) {
        if (width < 0 || height < 0) {
            throw new IllegalArgumentException("Size cannot be negative.");
        }
        mRatioWidth = width;
        mRatioHeight = height;
        requestLayout();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int height = MeasureSpec.getSize(heightMeasureSpec);
        if (0 == mRatioWidth || 0 == mRatioHeight) {
            setMeasuredDimension(width, height);
        } else {
            if (width < height * mRatioWidth / mRatioHeight) {
                setMeasuredDimension(width, width * mRatioHeight / mRatioWidth);
            } else {
                setMeasuredDimension(height * mRatioWidth / mRatioHeight, height);
            }
        }
    }


    /**
     * 获取相机实例
     */
    private Camera getCameraInstance() {
        Camera camera = null;
        try {
            // 获取相机实例, 注意:某些设备厂商可能需要用 Camera.open() 方法才能打开相机。
            camera = Camera.open(mCurrentCameraFacing);
        } catch (Exception e) {
            // 相机不可用或不存在
            Log.e(TAG, "error open(int cameraId) camera1 : " + e.getMessage());
        }

        try {
            if (null == camera)
                camera = Camera.open();
        } catch (Exception e) {
            Log.e(TAG, "error open camera1() : " + e.getMessage());
        }

        return camera;
    }

    /**
     * 释放相机
     */
    private void releaseCamera() {
        if (mCamera != null) {
            mCamera.stopPreview();
            mCamera.release();        // release the camera1 for other applications
            mCamera = null;
        }
    }


    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        Log.i(TAG, "surfaceCreated");
        // Surface创建完成, 现在即可设置相机预览
        startPreview(holder);
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

        Log.i(TAG, "surfaceChanged   format" + format + ", width =" + width + " | height=" + height);
        // surface在改变大小或旋转时触发此时间
        // 确保在调整或重新格式化之前停止视频预览
        if (mSurfaceHolder.getSurface() == null) {
            // 预览Surface不存在
            return;
        }
        // 改变前先停止预览
        try {
            mCamera.stopPreview();
            startPreview(holder);
            setCameraParameters(width, height);
        } catch (Exception e) {
            Log.d(TAG, "error starting camera1 preview: " + e.getMessage());
        }

    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        Log.i(TAG, "surfaceDestroyed");
        //释放相机
        releaseCamera();
    }


    private void startPreview(SurfaceHolder holder) {
        if (mCamera == null)
            mCamera = getCameraInstance();
        try {
            mCamera.setPreviewDisplay(holder);
            //设置预览的旋转角度
            mCamera.setDisplayOrientation(calcDisplayOrientation(mCurrentCameraFacing));
            mCamera.startPreview();
        } catch (IOException e) {
            Log.e(TAG, "error setting camera1 preview:" + e.getMessage());
        }
    }


    private int calcDisplayOrientation(int cameraId) {
        Camera.CameraInfo info =
                new Camera.CameraInfo();
        Camera.getCameraInfo(cameraId, info);
        int rotation = ((Activity) mContext).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;
        }
        mDisplayOrientation = degrees;
        int result;
        if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
            result = (info.orientation + degrees) % 360;
            result = (360 - result) % 360;  // compensate the mirror
        } else {                            // back-facing
            result = (info.orientation - degrees + 360) % 360;
        }
        return result;
    }


    private Camera.Parameters mCameraParameters;
    private final SizeMap mPreviewSizes = new SizeMap();
    private final SizeMap mPictureSizes = new SizeMap();
    private AspectRatio mAspectRatio;

    private int mDisplayOrientation;

    private AspectRatio chooseAspectRatio() {
        AspectRatio r = null;
        for (AspectRatio ratio : mPreviewSizes.ratios()) {
            r = ratio;
            if (ratio.equals(Constants.DEFAULT_ASPECT_RATIO)) {
                return ratio;
            }
        }
        return r;
    }

    private Size chooseOptimalSize(SortedSet<Size> sizes, int surfaceWidth, int surfaceHeight) {

        int desiredWidth;
        int desiredHeight;
        if (isLandscape(mDisplayOrientation)) {
            desiredWidth = surfaceHeight;
            desiredHeight = surfaceWidth;
        } else {
            desiredWidth = surfaceWidth;
            desiredHeight = surfaceHeight;
        }
        Size result = null;
        for (Size size : sizes) { // Iterate from small to large
            if (desiredWidth <= size.getWidth() && desiredHeight <= size.getHeight()) {
                return size;

            }
            result = size;
        }
        return result;
    }

    /**
     * Test if the supplied orientation is in landscape.
     *
     * @param orientationDegrees Orientation in degrees (0,90,180,270)
     * @return True if in landscape, false if portrait
     */
    private boolean isLandscape(int orientationDegrees) {
        return (orientationDegrees == Constants.LANDSCAPE_90 ||
                orientationDegrees == Constants.LANDSCAPE_270);
    }

    /***
     * 设置相机参数
     *
     * @param width
     * @param height
     */
    private void setCameraParameters(int width, int height) {
        if (mCamera == null)
            return;

        mCameraParameters = mCamera.getParameters();


        // 相机预览支持的大小
        mPreviewSizes.clear();
        for (Camera.Size size : mCameraParameters.getSupportedPreviewSizes()) {
            mPreviewSizes.add(new Size(size.width, size.height));
        }
        // 相机照片支持的大小
        mPictureSizes.clear();
        for (Camera.Size size : mCameraParameters.getSupportedPictureSizes()) {
            mPictureSizes.add(new Size(size.width, size.height));
        }
        // AspectRatio  默认长宽比为4:3
        if (mAspectRatio == null) {
            mAspectRatio = Constants.DEFAULT_ASPECT_RATIO;
        }

        SortedSet<Size> sizes = mPreviewSizes.sizes(mAspectRatio);
        if (sizes == null) { // Not supported
            mAspectRatio = chooseAspectRatio();
            sizes = mPreviewSizes.sizes(mAspectRatio);
        }
        Size previewSize = chooseOptimalSize(sizes, width, height);

        // Always re-apply camera1 parameters
        // Largest picture size in this ratio
        final Size pictureSize = mPictureSizes.sizes(mAspectRatio).last();

        mCameraParameters.setPreviewSize(previewSize.getWidth(), previewSize.getHeight());
        mCameraParameters.setPictureSize(pictureSize.getWidth(), pictureSize.getHeight());


        mCameraParameters.setPictureFormat(ImageFormat.JPEG); // 设置图片格式
        mCameraParameters.setJpegQuality(100); // 设置照片质量
        mCameraParameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);//自动对焦
        //parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);//连续对焦
        //camera1.cancelAutoFocus();//如果要实现连续的自动对焦,这一句必须加上

        mCamera.setParameters(mCameraParameters);


        //根据我们选中的预览相机大小的宽高比调整View的大小
        int orientation = getResources().getConfiguration().orientation;
        if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
            setAspectRatio(
                    previewSize.getWidth(), previewSize.getHeight());
        } else {
            setAspectRatio(
                    previewSize.getHeight(), previewSize.getWidth());
        }

    }


    private String mPictureSaveDir;
    private TakePictureCallback takePictureCallback;

    private Camera.PictureCallback mPictureCallback = new Camera.PictureCallback() {

        @Override
        public void onPictureTaken(final byte[] data, Camera camera) {
            Log.d(TAG, "onPictureTaken start timestemp :" + System.currentTimeMillis());
            savePictureToSDCard(data);
            startPreview(mSurfaceHolder);
            isPictureCaptureInProgress.set(false);
            Log.d(TAG, "onPictureTaken end timestemp :" + System.currentTimeMillis());
        }
    };


    /**
     * 将拍下来的照片存放在SD卡中
     *
     * @param data
     */

    private void savePictureToSDCard(byte[] data) {
        File pictureFile;
        //检测外部存储是否存在
        if (FileUtils.checkSDCard()) {
            if (mPictureSaveDir == null) {
                pictureFile = FileUtils.getOutputMediaFile(mContext, FileUtils.MEDIA_TYPE_IMAGE);
            } else {
                pictureFile = FileUtils.getTimeStampMediaFile(mPictureSaveDir, FileUtils.MEDIA_TYPE_IMAGE);
            }
            if (pictureFile == null) {
                Log.e(TAG, "error creating media file, check storage permissions");
                if (takePictureCallback != null) {
                    takePictureCallback.error("error creating media file, check storage permissions");
                }
                return;
            }
        } else {
            pictureFile = FileUtils.getOutputMediaFile(mContext, FileUtils.MEDIA_TYPE_IMAGE);
        }

        try {
            FileOutputStream outputStream = new FileOutputStream(pictureFile);
            //由于在预览的时候,我们调整了预览的方向,所以在保存的时候我们要旋转回来,不然保存的图片方向是不正确的
            Matrix matrix = new Matrix();
            if (mCurrentCameraFacing == Camera.CameraInfo.CAMERA_FACING_BACK) {
                matrix.setRotate(90);
            } else {
                matrix.setRotate(-90);
            }
            Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
            bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, false);
            bitmap.compress(Bitmap.CompressFormat.JPEG, 80, outputStream);
            outputStream.write(data);
            outputStream.close();
        } catch (Exception e) {
            Log.e(TAG, "savePictureToSDCard error :" + e.getMessage());
            if (takePictureCallback != null) {
                takePictureCallback.error(e.getMessage());
            }
            return;
        }

        if (takePictureCallback != null)
            takePictureCallback.success(pictureFile.getAbsolutePath());

        //这个的作用是让系统去扫描刚拍下的这个图片文件,以利于在MediaSore中能及时更新,
        // 可能会存在部分手机不用使用的情况(众所周知,现在国内的Rom厂商已把原生Rom改的面目全非)
        //mContext.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse("file://" + mPictureSavePath)));
//        MediaScannerConnection.scanFile(mContext, new String[]{
//                        pictureFile.getAbsolutePath()},
//                null, new MediaScannerConnection.OnScanCompletedListener() {
//                    @Override
//                    public void onScanCompleted(String path, Uri uri) {
////                         Log.e(TAG, "扫描完成");
//                    }
//                });
    }


}


Camera2 API的封装及其实现

Camera2的封装这里我用了TextureView 。其中有关Camera2 API的具体的使用方式和解读可在我的这一篇博文中找到: Android Camera2相机使用流程讲解

这里我就直接贴出源码啦。

public class Camera2Preview extends TextureView implements CameraView {


    private static final String TAG = "Camera2Preview";
    private Context mContext;
    private WindowManager mWindowManager;


    private int mRatioWidth = 0;
    private int mRatioHeight = 0;

    private int mCameraCount;
    private int mCurrentCameraFacing = CameraCharacteristics.LENS_FACING_BACK;


    public Camera2Preview(Context context) {
        this(context, null);
    }

    public Camera2Preview(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public Camera2Preview(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        mContext = context;
        init();
    }

    private void init() {
        mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
    }


    /**
     * 同生命周期 onResume
     */
    @Override
    public void onResume() {
        startBackgroundThread();
        // 当关闭并打开屏幕时,SurfaceTexture已经可用,并且不会调用“onSurfaceTextureAvailable”。
        // 在这种情况下,我们可以打开相机并从这里开始预览(否则,我们将等待,直到Surface在SurfaceTextureListener中准备好)。
        if (isAvailable()) {
            openCamera(getWidth(), getHeight());
        } else {
            setSurfaceTextureListener(mSurfaceTextureListener);
        }
    }

    /**
     * 同生命周期 onPause
     */
    @Override
    public void onPause() {
        closeCamera();
        stopBackgroundThread();
    }


    /**
     * 启动拍照的方法
     */

    @Override
    public void takePicture() {
        takePictureInternal();
    }

    private TakePictureCallback mTakePictureCallback;

    @Override
    public void takePicture(TakePictureCallback takePictureCallback) {
        mTakePictureCallback = takePictureCallback;
        takePictureInternal();
    }

    private void takePictureInternal() {
        if (mCurrentCameraFacing == CameraCharacteristics.LENS_FACING_BACK) {
            lockFocus();
        } else {
            runPrecaptureSequence();
        }
    }


    /**
     * 设置图片的保存路径(文件夹)
     *
     * @param pictureSavePath
     */
    private String mPictureSaveDir;

    @Override
    public void setPictureSavePath(String pictureSavePath) {
        mPictureSaveDir = pictureSavePath;
    }

    @Override
    public void switchCameraFacing() {
        if (mCameraCount > 1) {
            mCurrentCameraFacing = mCurrentCameraFacing == CameraCharacteristics.LENS_FACING_BACK ?
                    CameraCharacteristics.LENS_FACING_FRONT : CameraCharacteristics.LENS_FACING_BACK;
            closeCamera();
            openCamera(getWidth(), getHeight());
        }
    }


    /**
     * 设置此视图的宽高比。
     * 视图的大小将基于从参数计算的比率来测量。
     * 请注意,参数的实际大小并不重要,也就是说,setAspectRatio(2, 3)setAspectRatio(4, 6)会得到相同的结果。
     *
     * @param width  Relative horizontal size
     * @param height Relative vertical size
     */
    public void setAspectRatio(int width, int height) {
        if (width < 0 || height < 0) {
            throw new IllegalArgumentException("Size cannot be negative.");
        }
        mRatioWidth = width;
        mRatioHeight = height;
        requestLayout();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int height = MeasureSpec.getSize(heightMeasureSpec);
        if (0 == mRatioWidth || 0 == mRatioHeight) {
            setMeasuredDimension(width, height);
        } else {
            if (width < height * mRatioWidth / mRatioHeight) {
                setMeasuredDimension(width, width * mRatioHeight / mRatioWidth);
            } else {
                setMeasuredDimension(height * mRatioWidth / mRatioHeight, height);
            }
        }
    }


    private static final SparseIntArray ORIENTATIONS = new SparseIntArray();

    static {
        ORIENTATIONS.append(Surface.ROTATION_0, 90);
        ORIENTATIONS.append(Surface.ROTATION_90, 0);
        ORIENTATIONS.append(Surface.ROTATION_180, 270);
        ORIENTATIONS.append(Surface.ROTATION_270, 180);
    }


    /**
     * 相机状态: 显示相机预览
     */
    private static final int STATE_PREVIEW = 0;
    /**
     * 相机状态: 等待焦点被锁定
     */
    private static final int STATE_WAITING_LOCK = 1;
    /**
     * 相机状态: 等待曝光前的状态。
     */
    private static final int STATE_WAITING_PRECAPTURE = 2;
    /**
     * 相机状态: 等待曝光状态非预先捕获的东西.
     */
    private static final int STATE_WAITING_NON_PRECAPTURE = 3;
    /**
     * 相机状态: 照片拍摄
     */
    private static final int STATE_PICTURE_TAKEN = 4;

    /**
     * 最大的预览宽度
     */
    private static final int MAX_PREVIEW_WIDTH = 1920;

    /**
     * 最大的预览高度
     */
    private static final int MAX_PREVIEW_HEIGHT = 1080;

    /**
     * {@link TextureView.SurfaceTextureListener} handles several lifecycle events on a
     * {@link TextureView}.
     */
    private final TextureView.SurfaceTextureListener mSurfaceTextureListener
            = new TextureView.SurfaceTextureListener() {

        @Override
        public void onSurfaceTextureAvailable(SurfaceTexture texture, int width, int height) {
            openCamera(width, height);
        }

        @Override
        public void onSurfaceTextureSizeChanged(SurfaceTexture texture, int width, int height) {
            configureTransform(width, height);
        }

        @Override
        public boolean onSurfaceTextureDestroyed(SurfaceTexture texture) {
            return true;
        }

        @Override
        public void onSurfaceTextureUpdated(SurfaceTexture texture) {
        }

    };

    /**
     * ID of the current {@link CameraDevice}.
     */
    private String mCameraId;

    /**
     * 相机预览要用到的{@link CameraCaptureSession } .
     */
    private CameraCaptureSession mCaptureSession;

    /**
     * A reference to the opened {@link CameraDevice}.
     */
    private CameraDevice mCameraDevice;

    /**
     * 相机预览的 {@link android.util.Size}
     */
    private Size mPreviewSize;

    /**
     * {@link CameraDevice.StateCallback} is called when {@link CameraDevice} changes its state.
     */
    private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {

        @Override
        public void onOpened(@NonNull CameraDevice cameraDevice) {
            // 当相机打开时调用此方法。我们在这里开始相机预览。
            mCameraOpenCloseLock.release();
            mCameraDevice = cameraDevice;
            createCameraPreviewSession();
        }

        @Override
        public void onDisconnected(@NonNull CameraDevice cameraDevice) {
            mCameraOpenCloseLock.release();
            cameraDevice.close();
            mCameraDevice = null;
        }

        @Override
        public void onError(@NonNull CameraDevice cameraDevice, int error) {
            mCameraOpenCloseLock.release();
            cameraDevice.close();
            mCameraDevice = null;
            Log.e(TAG, "CameraDevice.StateCallback onError errorCode= " + error);
        }

    };

    /**
     * 用于运行不应该阻止UI的任务的附加线程。
     */
    private HandlerThread mBackgroundThread;

    /**
     * 在后台运行任务的Handler。
     */
    private Handler mBackgroundHandler;

    /**
     * An {@link ImageReader} 用于处理图像捕获(抓拍).
     */
    private ImageReader mImageReader;


    /**
     * 图片文件
     */
    private File mPictureFile;
    /**
     * 这是{@link ImageReader}的回调对象. 当静态图像准备好保存时,将回调"onImageAvailable"
     */
    private final ImageReader.OnImageAvailableListener mOnImageAvailableListener
            = new ImageReader.OnImageAvailableListener() {

        @Override
        public void onImageAvailable(ImageReader reader) {
            //检测外部存储是否存在
            //根据时间戳生成图片名称
            if (FileUtils.checkSDCard()) {
                if (mPictureSaveDir == null) {
                    mPictureFile = FileUtils.getOutputMediaFile(mContext, FileUtils.MEDIA_TYPE_IMAGE);
                } else {
                    mPictureFile = FileUtils.getTimeStampMediaFile(mPictureSaveDir, FileUtils.MEDIA_TYPE_IMAGE);
                }
                if (mPictureFile == null) {
                    Log.e(TAG, "error creating media file, check storage permissions");
                    if (mTakePictureCallback != null) {
                        mTakePictureCallback.error("error creating media file, check storage permissions");
                    }
                    return;
                }
            } else {
                mPictureFile = FileUtils.getOutputMediaFile(mContext, FileUtils.MEDIA_TYPE_IMAGE);
            }
            mBackgroundHandler.post(new ImageSaver(reader.acquireNextImage(), mPictureFile));
        }

    };

    /**
     * {@link CaptureRequest.Builder} for the camera1 preview
     */
    private CaptureRequest.Builder mPreviewRequestBuilder;

    /**
     * {@link CaptureRequest} generated by {@link #mPreviewRequestBuilder}
     */
    private CaptureRequest mPreviewRequest;

    /**
     * 拍照相机的当前状态
     *
     * @see #mCaptureCallback
     */
    private int mState = STATE_PREVIEW;

    /**
     * A {@link Semaphore} 防止相机关闭前退出应用程序。
     */
    private Semaphore mCameraOpenCloseLock = new Semaphore(1);

    /**
     * 当前的相机设备是否支持Flash。
     */
    private boolean mFlashSupported;

    /**
     * 相机传感器的方向
     */
    private int mSensorOrientation;

    /**
     * A {@link CameraCaptureSession.CaptureCallback} 处理JPEG捕获的相关事件
     */
    private CameraCaptureSession.CaptureCallback mCaptureCallback
            = new CameraCaptureSession.CaptureCallback() {

        private void process(CaptureResult result) {
            switch (mState) {
                case STATE_PREVIEW: {
                    // We have nothing to do when the camera1 preview is working normally.
                    break;
                }
                case STATE_WAITING_LOCK: {
                    Integer afState = result.get(CaptureResult.CONTROL_AF_STATE);
                    if (afState == null) {
                        captureStillPicture();
                    } else if (CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED == afState ||
                            CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED == afState) {
                        // CONTROL_AE_STATE can be null on some devices
                        Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
                        if (aeState == null ||
                                aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED) {
                            mState = STATE_PICTURE_TAKEN;
                            captureStillPicture();
                        } else {
                            runPrecaptureSequence();
                        }
                    }
                    break;
                }
                case STATE_WAITING_PRECAPTURE: {
                    // CONTROL_AE_STATE can be null on some devices
                    Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
                    if (aeState == null ||
                            aeState == CaptureResult.CONTROL_AE_STATE_PRECAPTURE ||
                            aeState == CaptureRequest.CONTROL_AE_STATE_FLASH_REQUIRED) {
                        mState = STATE_WAITING_NON_PRECAPTURE;
                    }
                    break;
                }
                case STATE_WAITING_NON_PRECAPTURE: {
                    // CONTROL_AE_STATE can be null on some devices
                    Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
                    if (aeState == null || aeState != CaptureResult.CONTROL_AE_STATE_PRECAPTURE) {
                        mState = STATE_PICTURE_TAKEN;
                        captureStillPicture();
                    }
                    break;
                }
            }
        }

        @Override
        public void onCaptureProgressed(@NonNull CameraCaptureSession session,
                                        @NonNull CaptureRequest request,
                                        @NonNull CaptureResult partialResult) {
            process(partialResult);
        }

        @Override
        public void onCaptureCompleted(@NonNull CameraCaptureSession session,
                                       @NonNull CaptureRequest request,
                                       @NonNull TotalCaptureResult result) {
            process(result);
        }

    };


    /**
     * 给定相机支持的{@code Size}的{@code choices},
     * 选择至少与相应TextureView大小一样大、最多与相应最大大小一样大、且纵横比与指定值匹配的最小一个。
     * 如果不存在这样的尺寸,则选择最大尺寸与相应的最大尺寸一样大,并且其纵横比与指定值匹配的最大尺寸。
     *
     * @param choices           摄像机支持预期输出类的大小列表。
     * @param textureViewWidth  TextureView相对于传感器坐标的宽度
     * @param textureViewHeight TextureView相对于传感器坐标的高度
     * @param maxWidth          可选择的最大宽度
     * @param maxHeight         可选择的最大高度
     * @param aspectRatio       宽高比
     * @return 最佳的 {@code Size}, 或任意一个,如果没有足够大的话
     */
    private static Size chooseOptimalSize(Size[] choices, int textureViewWidth,
                                          int textureViewHeight, int maxWidth, int maxHeight, Size aspectRatio) {

        // 收集至少与预览表面一样大的支持分辨率。
        List<Size> bigEnough = new ArrayList<>();
        // 收集小于预览表面的支持的分辨率
        List<Size> notBigEnough = new ArrayList<>();
        int w = aspectRatio.getWidth();
        int h = aspectRatio.getHeight();
        for (Size option : choices) {
            if (option.getWidth() <= maxWidth && option.getHeight() <= maxHeight &&
                    option.getHeight() == option.getWidth() * h / w) {
                if (option.getWidth() >= textureViewWidth &&
                        option.getHeight() >= textureViewHeight) {
                    bigEnough.add(option);
                } else {
                    notBigEnough.add(option);
                }
            }
        }

        // 挑那些最小中的足够大的。如果没有足够大的,选择最大的那些不够大的。
        if (bigEnough.size() > 0) {
            return Collections.min(bigEnough, new CompareSizesByArea());
        } else if (notBigEnough.size() > 0) {
            return Collections.max(notBigEnough, new CompareSizesByArea());
        } else {
            Log.e(TAG, "Couldn't find any suitable preview size");
            return choices[0];
        }
    }


    /**
     * 设置与摄像机相关的成员变量。
     *
     * @param width  相机预览可用尺寸的宽度
     * @param height 相机预览可用尺寸的高度
     */
    @SuppressWarnings("SuspiciousNameCombination")
    private void setUpCameraOutputs(int width, int height) {
        CameraManager manager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
        try {
            String[] cameraIdList = manager.getCameraIdList();
            mCameraCount = cameraIdList.length;
            for (String cameraId : cameraIdList) {
                CameraCharacteristics characteristics
                        = manager.getCameraCharacteristics(cameraId);

                //判断当前摄像头是前置还是后置摄像头
                Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING);
                if (facing != null && facing != mCurrentCameraFacing) {
                    continue;
                }

                StreamConfigurationMap map = characteristics.get(
                        CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
                if (map == null) {
                    continue;
                }

                // 对于静态图像捕获,我们使用最大可用的大小。
                Size largest = Collections.max(
                        Arrays.asList(map.getOutputSizes(ImageFormat.JPEG)),
                        new CompareSizesByArea());
                mImageReader = ImageReader.newInstance(largest.getWidth(), largest.getHeight(),
                        ImageFormat.JPEG, /*maxImages*/2);
                mImageReader.setOnImageAvailableListener(
                        mOnImageAvailableListener, mBackgroundHandler);

                // 找出是否需要交换尺寸以获得相对与传感器坐标的预览大小
                int displayRotation = mWindowManager.getDefaultDisplay().getRotation();
                //检验条件
                mSensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
                boolean swappedDimensions = false;
                switch (displayRotation) {
                    case Surface.ROTATION_0:
                    case Surface.ROTATION_180:
                        if (mSensorOrientation == 90 || mSensorOrientation == 270) {
                            swappedDimensions = true;
                        }
                        break;
                    case Surface.ROTATION_90:
                    case Surface.ROTATION_270:
                        if (mSensorOrientation == 0 || mSensorOrientation == 180) {
                            swappedDimensions = true;
                        }
                        break;
                    default:
                        Log.e(TAG, "Display rotation is invalid: " + displayRotation);
                }

                Point displaySize = new Point();
                mWindowManager.getDefaultDisplay().getSize(displaySize);
                int rotatedPreviewWidth = width;
                int rotatedPreviewHeight = height;
                int maxPreviewWidth = displaySize.x;
                int maxPreviewHeight = displaySize.y;

                if (swappedDimensions) {
                    rotatedPreviewWidth = height;
                    rotatedPreviewHeight = width;
                    maxPreviewWidth = displaySize.y;
                    maxPreviewHeight = displaySize.x;
                }

                if (maxPreviewWidth > MAX_PREVIEW_WIDTH) {
                    maxPreviewWidth = MAX_PREVIEW_WIDTH;
                }

                if (maxPreviewHeight > MAX_PREVIEW_HEIGHT) {
                    maxPreviewHeight = MAX_PREVIEW_HEIGHT;
                }

                // 危险,W.R.!尝试使用太大的预览大小可能超过相机总线的带宽限制,导致高清的预览,但存储垃圾捕获数据。
                mPreviewSize = chooseOptimalSize(map.getOutputSizes(SurfaceTexture.class),
                        rotatedPreviewWidth, rotatedPreviewHeight, maxPreviewWidth,
                        maxPreviewHeight, largest);

                // 我们将TextureView的宽高比与我们选择的预览大小相匹配。
                int orientation = getResources().getConfiguration().orientation;
                if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
                    setAspectRatio(
                            mPreviewSize.getWidth(), mPreviewSize.getHeight());
                } else {
                    setAspectRatio(
                            mPreviewSize.getHeight(), mPreviewSize.getWidth());
                }

                //检验是否支持flash
                Boolean available = characteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE);
                mFlashSupported = available == null ? false : available;

                mCameraId = cameraId;
                return;
            }
        } catch (CameraAccessException e) {
            e.printStackTrace();
        } catch (NullPointerException e) {
            //抛出空指针一般代表当前设备不支持Camera2API
            Log.e(TAG, "This device doesn't support Camera2 API.");

        }
    }

    /**
     * 打开指定的相机(mCameraId)
     */
    private void openCamera(int width, int height) {

        setUpCameraOutputs(width, height);
        configureTransform(width, height);

        CameraManager manager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
        try {
            if (!mCameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) {
                throw new RuntimeException("Time out waiting to lock camera1 opening.");
            }
            if (ActivityCompat.checkSelfPermission(mContext, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
                return;
            }
            manager.openCamera(mCameraId, mStateCallback, mBackgroundHandler);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            throw new RuntimeException("Interrupted while trying to lock camera1 opening.", e);
        }
    }

    /**
     * 关闭相机
     */
    private void closeCamera() {
        try {
            mCameraOpenCloseLock.acquire();
            if (null != mCaptureSession) {
                mCaptureSession.close();
                mCaptureSession = null;
            }
            if (null != mCameraDevice) {
                mCameraDevice.close();
                mCameraDevice = null;
            }
            if (null != mImageReader) {
                mImageReader.close();
                mImageReader = null;
            }
        } catch (InterruptedException e) {
            throw new RuntimeException("Interrupted while trying to lock camera1 closing.", e);
        } finally {
            mCameraOpenCloseLock.release();
        }
    }

    /**
     * 启动后台线程和Handler.
     */
    private void startBackgroundThread() {
        mBackgroundThread = new HandlerThread("CameraBackground");
        mBackgroundThread.start();
        mBackgroundHandler = new Handler(mBackgroundThread.getLooper());
    }

    /**
     * 停止后台线程和Handler.
     */
    private void stopBackgroundThread() {
        mBackgroundThread.quitSafely();
        try {
            mBackgroundThread.join();
            mBackgroundThread = null;
            mBackgroundHandler = null;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * 创建一个新的 {@link CameraCaptureSession} 用于相机预览.
     */
    private void createCameraPreviewSession() {
        try {
            SurfaceTexture texture = getSurfaceTexture();
            assert texture != null;

            // 我们将默认缓冲区的大小设置为我们想要的相机预览的大小。
            texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());

            // 我们需要开始预览输出Surface
            Surface surface = new Surface(texture);

            // 我们建立了一个具有输出Surface的捕获器。
            mPreviewRequestBuilder
                    = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
            mPreviewRequestBuilder.addTarget(surface);

            // 这里,我们创建了一个用于相机预览的CameraCaptureSession
            mCameraDevice.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()),
                    new CameraCaptureSession.StateCallback() {

                        @Override
                        public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {
                            // 相机已经关闭
                            if (null == mCameraDevice) {
                                return;
                            }

                            // 当session准备好后,我们开始显示预览
                            mCaptureSession = cameraCaptureSession;
                            try {
                                // 相机预览时应连续自动对焦
                                mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,
                                        CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
                                // 设置闪光灯在必要时自动打开
                                setAutoFlash(mPreviewRequestBuilder);

                                // 最终,显示相机预览
                                mPreviewRequest = mPreviewRequestBuilder.build();
                                mCaptureSession.setRepeatingRequest(mPreviewRequest,
                                        mCaptureCallback, mBackgroundHandler);
                            } catch (CameraAccessException e) {
                                e.printStackTrace();
                            }
                        }

                        @Override
                        public void onConfigureFailed(
                                @NonNull CameraCaptureSession cameraCaptureSession) {
                            Log.e(TAG, "CameraCaptureSession.StateCallback onConfigureFailed");
                        }
                    }, null
            );
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

    /**
     * 配置必要的 {@link android.graphics.Matrix} 转换为 `mTextureView`.
     * <p>
     * 该方法应该在setUpCameraOutputs中确定相机预览大小以及“mTextureView”的大小固定之后调用。
     *
     * @param viewWidth  The width of `mTextureView`
     * @param viewHeight The height of `mTextureView`
     */
    private void configureTransform(int viewWidth, int viewHeight) {
        if (null == mPreviewSize || null == mContext) {
            return;
        }

        int rotation = mWindowManager.getDefaultDisplay().getRotation();
        Matrix matrix = new Matrix();
        RectF viewRect = new RectF(0, 0, viewWidth, viewHeight);
        RectF bufferRect = new RectF(0, 0, mPreviewSize.getHeight(), mPreviewSize.getWidth());
        float centerX = viewRect.centerX();
        float centerY = viewRect.centerY();
        if (Surface.ROTATION_90 == rotation || Surface.ROTATION_270 == rotation) {
            bufferRect.offset(centerX - bufferRect.centerX(), centerY - bufferRect.centerY());
            matrix.setRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.FILL);
            float scale = Math.max(
                    (float) viewHeight / mPreviewSize.getHeight(),
                    (float) viewWidth / mPreviewSize.getWidth());
            matrix.postScale(scale, scale, centerX, centerY);
            matrix.postRotate(90 * (rotation - 2), centerX, centerY);
        } else if (Surface.ROTATION_180 == rotation) {
            matrix.postRotate(180, centerX, centerY);
        }
        setTransform(matrix);
    }


    /**
     * 锁定焦点作为静态图像捕获的第一步
     */
    private void lockFocus() {
        try {
            //  这里是让相机锁定焦点
            mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,
                    CameraMetadata.CONTROL_AF_TRIGGER_START);
            // 告知 #mCaptureCallback 等待锁
            mState = STATE_WAITING_LOCK;
            mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback,
                    mBackgroundHandler);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

    /**
     * 运行预捕获序列捕获一张静态图片。
     * <p>
     * 这个方法应该在我们从得到mCaptureCallback的响应后调用
     */
    private void runPrecaptureSequence() {
        try {
            // 这就是如何告诉相机触发。
            mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
                    CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START);
            // 告知 #mCaptureCallback 等待设置预捕获序列。
            mState = STATE_WAITING_PRECAPTURE;
            mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback,
                    mBackgroundHandler);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

    /**
     * 捕获一张静态图片
     * 这个方法应该在我们从得到mCaptureCallback的响应后调用
     */
    private void captureStillPicture() {
        try {
            if (null == mCameraDevice) {
                return;
            }
            // 这是 CaptureRequest.Builder ,我们用它来进行拍照
            final CaptureRequest.Builder captureBuilder =
                    mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
            captureBuilder.addTarget(mImageReader.getSurface());

            //  使用相同的AE和AF模式作为预览。
            captureBuilder.set(CaptureRequest.CONTROL_AF_MODE,
                    CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
            setAutoFlash(captureBuilder);

            // 方向
            int rotation = mWindowManager.getDefaultDisplay().getRotation();
            captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, getOrientation(rotation));

            CameraCaptureSession.CaptureCallback CaptureCallback
                    = new CameraCaptureSession.CaptureCallback() {

                @Override
                public void onCaptureCompleted(@NonNull CameraCaptureSession session,
                                               @NonNull CaptureRequest request,
                                               @NonNull TotalCaptureResult result) {
                    Log.d(TAG, "CameraCaptureSession.CaptureCallback onCaptureCompleted 图片保存地址为:" + mPictureFile.toString());
                    if (mTakePictureCallback != null) {
                        mTakePictureCallback.success(mPictureFile.getAbsolutePath());
                    }
                    unlockFocus();
                }
            };

            mCaptureSession.stopRepeating();
            mCaptureSession.abortCaptures();
            mCaptureSession.capture(captureBuilder.build(), CaptureCallback, null);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

    /**
     * 从指定的屏幕旋转中检索JPEG方向。
     *
     * @param rotation 图片旋转
     * @return The JPEG orientation (one of 0, 90, 270, and 360)
     */
    private int getOrientation(int rotation) {
        // 对于大多数设备,传感器定向是90,对于某些设备(例如Nexus 5X)是270。
        //我们必须考虑到这一点,并适当的旋转JPEG。
        //对于取向为90的设备,我们只需从方向返回映射即可。
        //对于方向为270的设备,我们需要旋转JPEG 180度。
        return (ORIENTATIONS.get(rotation) + mSensorOrientation + 270) % 360;
    }

    /**
     * 解锁焦点.
     * <p>
     * 此方法应该在静态图片捕获序列结束后调用
     */
    private void unlockFocus() {
        try {
            // 重置自动对焦触发
            mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,
                    CameraMetadata.CONTROL_AF_TRIGGER_CANCEL);
            setAutoFlash(mPreviewRequestBuilder);
            mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback,
                    mBackgroundHandler);
            // 在此之后,相机将回到正常的预览状态。
            mState = STATE_PREVIEW;
            mCaptureSession.setRepeatingRequest(mPreviewRequest, mCaptureCallback,
                    mBackgroundHandler);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }


    private void setAutoFlash(CaptureRequest.Builder requestBuilder) {
        if (mFlashSupported) {
            requestBuilder.set(CaptureRequest.CONTROL_AE_MODE,
                    CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
        }
    }

    /**
     * 将JPEG{@link Image}保存并放到指定的文件中
     */
    private static class ImageSaver implements Runnable {

        /**
         * The JPEG image
         */
        private final Image mImage;
        /**
         * The file we save the image into.
         */
        private final File mFile;

        ImageSaver(Image image, File file) {
            mImage = image;
            mFile = file;
        }

        @Override
        public void run() {
            ByteBuffer buffer = mImage.getPlanes()[0].getBuffer();
            byte[] bytes = new byte[buffer.remaining()];
            buffer.get(bytes);
            FileOutputStream output = null;
            try {
                output = new FileOutputStream(mFile);
                output.write(bytes);
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                mImage.close();
                if (null != output) {
                    try {
                        output.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }

    }

    /**
     * 根据它们的区域比较两个的大小 {@code Size}。
     */
    static class CompareSizesByArea implements Comparator<Size> {

        @Override
        public int compare(Size lhs, Size rhs) {
            // We cast here to ensure the multiplications won't overflow
            return Long.signum((long) lhs.getWidth() * lhs.getHeight() -
                    (long) rhs.getWidth() * rhs.getHeight());
        }

    }

好了,Camera1和Camera2 我们已经用自定义View封装好了,其中用到的一些工具类方法小伙伴们可以直接从我的github源码中取得咯。而博客中我更想要分享的是我在封装代码中收获到的一些心得。
在封装相机API的过程中,我主要是从业务使用方式的简便性出发封装的Camera API。这里我也只是封装了相机的拍照功能,后续也可以加入相机的录像功能。

欢迎小伙伴们提出自己的想法和意见,我们可以互相交流共同进步。

猜你喜欢

转载自blog.csdn.net/Android_Technology/article/details/81538259