Android Studio 调用Camera实现拍照功能

最近总想写写,但又不知写些什么,想了想,今天写个Camera拍照的教程吧。本例子的流程为   首先通过SurfaceView将Camera的实时画面显示在屏幕上,然后通过点击拍照对当前画面进行捕捉,最后将获得的图片保存至本地。

  1. 首先创建一个SurfaceHolder实现对SurfaceView的回调,然后重写SurfaceCreate函数,实现对Camera的初始化等一系列工作:代码如下:
    @Override
            public void surfaceCreated(SurfaceHolder holder) {
                Log.e("TAG","------surfaceCreated------");
                try {
                    //这里我优先找后置摄像头,找不到再找前面的
                    int cameraIndex = findBackOrFrontCamera(Camera.CameraInfo.CAMERA_FACING_BACK);
                    if (cameraIndex == -1) {
                        cameraIndex = findBackOrFrontCamera(Camera.CameraInfo.CAMERA_FACING_FRONT);
                        if (cameraIndex == -1) {
                            Log.e("TAG", "No Camera!");
                            currentCameraType = CAMERA_NOTEXIST;
                            currentCameraIndex = -1;
                            return;
                        } else {
                            currentCameraType = FRONT;
                        }
                    } else {
                        currentCameraType = BACK;
                    }
    
                    //找到想要的摄像头后,就打开
                    if (mCamera == null) {
                        mCamera = openCamera(currentCameraType);
                    }
    
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
    

    本例子中,我首先找到想要打开的摄像头,这里的优先寻找后置摄像头,如果没有找到,再找前置的,代码如下:

        /**
         * 按要求查找摄像头
         *
         * @param camera_facing 按要求查找,镜头是前还是后
         * @return -1表示找不到
         */
        private int findBackOrFrontCamera(int camera_facing) {
            int cameraCount = 0;
            Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
            cameraCount = Camera.getNumberOfCameras();
            for (int camIdx = 0; camIdx < cameraCount; camIdx++) {
                Camera.getCameraInfo(camIdx, cameraInfo);
                if (cameraInfo.facing == camera_facing) {
                    return camIdx;
                }
            }
            return -1;
        }
    

    当找到摄像头后,便打开Camera,其实打开Camera可以直接用open(CameraId)函数即可,但我在重新封装了一下,直接帖代码:

    
        /**
         * 按照type的类型打开相应的摄像头
         *
         * @param type 标志当前打开前还是后的摄像头
         * @return 返回当前打开摄像机的对象
         */
        private Camera openCamera(int type) {
            int frontIndex = -1;
            int backIndex = -1;
            int cameraCount = Camera.getNumberOfCameras();
    
            Camera.CameraInfo info = new Camera.CameraInfo();
            for (int cameraIndex = 0; cameraIndex < cameraCount; cameraIndex++) {
                Camera.getCameraInfo(cameraIndex, info);
                if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
                    frontIndex = cameraIndex;
                } else if (info.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
                    backIndex = cameraIndex;
                }
            }
    
            currentCameraType = type;
            if (type == FRONT && frontIndex != -1) {
                currentCameraIndex = frontIndex;
                return Camera.open(frontIndex);
            } else if (type == BACK && backIndex != -1) {
                currentCameraIndex = backIndex;
                return Camera.open(backIndex);
            }
            return null;
        }
  2. 然后在SurfaceChange对Camera进行一系列初始化(对摄像头初始化,就是设定图片格式,图片尺寸等赋值,要打开摄像头才可以初始化,否则会报错)
        /**
         * 初始化摄像头
         * @param holder
         */
        private void initCamera(SurfaceHolder holder){
            Log.e("TAG","initCamera");
            if (mPreviewRunning)
                mCamera.stopPreview();
    
            Camera.Parameters parameters;
            try{
                //获取预览的各种分辨率
                parameters = mCamera.getParameters();
            }catch (Exception e){
                e.printStackTrace();
                return;
            }
            //这里我设为480*800的尺寸
            parameters.setPreviewSize(480,800);
            // 设置照片格式
            parameters.setPictureFormat(PixelFormat.JPEG);
            //设置图片预览的格式
            parameters.setPreviewFormat(PixelFormat.YCbCr_420_SP);
            setCameraDisplayOrientation(this,currentCameraIndex,mCamera);
            try{
                mCamera.setPreviewDisplay(holder);
            }catch(Exception e){
                if(mCamera != null){
                    mCamera.release();
                    mCamera = null;
                }
                e.printStackTrace();
            }
            mCamera.startPreview();
            mPreviewRunning = true;
        }
    
        /**
         * 设置旋转角度
         * @param activity
         * @param cameraId
         * @param camera
         */
        private 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;
            }else{
                result = (info.orientation - degrees +360) % 360;
            }
            camera.setDisplayOrientation(result);
        }
    
     
  3. 当这些工作完成后,便可以对当前摄像头捕捉的画面进行拍照了:
        /**
         * 实现拍照功能
         */
        public void takePhoto(){
            Camera.Parameters parameters;
            try{
                parameters = mCamera.getParameters();
            }catch(Exception e){
                e.printStackTrace();
                return;
            }
            //获取摄像头支持的各种分辨率,因为摄像头数组不确定是按降序还是升序,这里的逻辑有时不是很好找得到相应的尺寸
            //可先确定是按升还是降序排列,再进对对比吧,我这里拢统地找了个,是个不精确的...
            List<Camera.Size> list = parameters.getSupportedPictureSizes();
            int size = 0;
            for (int i =0 ;i < list.size() - 1;i++){
                if (list.get(i).width >= 480){
                    //完美匹配
                    size = i;
                    break;
                }
                else{
                    //找不到就找个最接近的吧
                    size = i;
                }
            }
            //设置照片分辨率,注意要在摄像头支持的范围内选择
            parameters.setPictureSize(list.get(size).width,list.get(size).height);
            //设置照相机参数
            mCamera.setParameters(parameters);
    
            //使用takePicture()方法完成拍照
            mCamera.autoFocus(new Camera.AutoFocusCallback() {
                //自动聚焦完成后拍照
                @Override
                public void onAutoFocus(boolean success, Camera camera) {
                    if (success && camera != null){
                        mCamera.takePicture(new ShutterCallback(), null, new Camera.PictureCallback() {
                            //拍照回调接口
                            @Override
                            public void onPictureTaken(byte[] data, Camera camera) {
                                savePhoto(data);
                                //停止预览
                                mCamera.stopPreview();
                                //重启预览
                                mCamera.startPreview();
                            }
                        });
                    }
                }
            });
        }
    
       /* *//**
         * 快门回调接口,如果不想拍照声音,直接将new ShutterCallback()修改为null即可
         */
        private class ShutterCallback implements Camera.ShutterCallback {
            @Override
            public void onShutter() {
                MediaPlayer mPlayer = new MediaPlayer();
                mPlayer = MediaPlayer.create(getApplicationContext(), R.raw.shutter);
                try{
                    mPlayer.prepare();
                }catch (IllegalStateException e){
                    e.printStackTrace();
                }catch (IOException e){
                    e.printStackTrace();
                }
                mPlayer.start();
            }
        }
    

    这里要注意下,有些手机调用onAutoFocus函数,会返回失败,因为如果该手机无自动对焦,则无法执行对焦成功后的函数了。。。

  4. 当捕捉到数据后,便可以将这些数据保存至设定的地方了:

        /**
         * 设置照片的路径,具体路径可自定义
         * @return
         */
        private String setPicSaveFile(){
            //创建保存的路径
            File storageDir = getOwnCacheDirectory(this,"MyCamera/photos");
            //返回自定义的路径
            return storageDir.getPath();
        }
    
        private File getOwnCacheDirectory(Context context, String cacheDir) {
            File appCacheDir = null;
            //判断SD卡正常挂载并且拥有根限的时候创建文件
            if(Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) &&
                    hasExternalStoragePermission(context)){
                appCacheDir = new File(Environment.getExternalStorageDirectory(),cacheDir);
            }
            if (appCacheDir == null || !appCacheDir.exists() && !appCacheDir.mkdirs()){
                appCacheDir = context.getCacheDir();
            }
            return appCacheDir;
        }
    
        /**
         * 检查是否有权限
         * @param context
         * @return
         */
        private boolean hasExternalStoragePermission(Context context) {
            int permission = context.checkCallingOrSelfPermission("android.permission.WRITE_EXTERNAL_STORAGE");
            //PERMISSION_GRANTED=0
            return permission == 0;
        }
    

    由于调用了系统Camera和对SD卡读写,所以在AndroidManifest需要申请权限:

    <!--摄像头相关权限-->
        <uses-permission android:name="android.permission.CAMERA" />
        <uses-feature android:name="android.hardware.camera" />
        <uses-feature android:name="android.hardware.camera.autofocus" />
        <!-- 在SDCard中创建与删除文件权限 -->
        <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
        <!-- 往SDCard写入数据权限 -->
        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    

后记:google在Android5.0后推出了Camera的升级版---Camera2:

按照Android的官方说明,camera 2支持以下5点新特性,有兴趣的可以研究下:

(1)支持每秒30帧的全高清连拍。
(2)支持在每帧之间使用不同的设置。
(3)支持原生格式的图像输出。
(4)支持零延迟快门和电影速拍。
(5)支持相机在其他方面的手动控制,比如设置噪音消除的级别。

最后也贴出本例子的源码吧,有兴趣的可以到Github上下载本例子;如果支持本文,也可以直接在CSDN本站上下载github的链接地址:CSDN地址:https://download.csdn.net/download/toyauko/10636251

github传送门:https://github.com/Liangzhuhua/MyCamera.git

猜你喜欢

转载自blog.csdn.net/toyauko/article/details/82220933