Android 相机学习

需求是万恶之源,今天我们来说说相机

调用系统相机

如果只是调起相机拍照,很简单就两步:

1.权限

    
    

2.创建意图跳转

     Intent intent =new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
     startActivity(intent);

但是我们一般都会需要获取图片数据,Android也给我们提供了调用的方式

1.替换跳转方式

    startActivityForResult(intent, REQUEST_CODE_CAPTURE_RAW);

2.监听返回

    /**
     * 图片拍照回调
     * @param requestCode
     * @param resultCode
     * @param data
     */
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        //通过data获取图片信息
    }

如果你使用过上面的方式你会发现得到图片质量并不高,这是由于现在相机的相机像素都很高,拍出来的照片所占用的内存(这里说的内存是bitmap占用的,不是图片在SD卡占用的,注意区分)都很高,容易oom,所以我们这里我们得到不是原图是缩略图

那如果需要原图怎么办了?这里也是有方式获取的

直接上代码

//拍照(返回原始图)
    public void gotoCaptureRaw() {
        imageFile = createImageFile(false);
        if (imageFile != null) {
            Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
            //获取设备sdk版本
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                Uri imgUri = FileProvider.getUriForFile(this, AUTHORITY, imageFile);
                intent.putExtra(MediaStore.EXTRA_OUTPUT, imgUri);
            } else {//7.0 以下
                //设置拍照图片保存路径
                intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(imageFile));
            }
            //设置图片保存格式
            intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
            ComponentName componentName = intent.resolveActivity(getPackageManager());
            //判断组件是否为null,避免崩溃
            if (componentName != null) {
                startActivityForResult(intent, REQUEST_CODE_CAPTURE_RAW);
            }
        }
    }

想要获取原图,就得指定拍摄照片后图片的存储路径
关键代码

 intent.putExtra(MediaStore.EXTRA_OUTPUT, imgUri);

注意7.0以上需要使用FileProvider,怎么使用这里就不多讲了,自行查阅资料
然后在onActivityResult回调中,通过imageFile获取图片,不过有一点需要注意,由于获取是原图,可能会导致oom,建议对图片压缩处理

简单拍个照,上面这些应该够用了.但有时系统相机并不满足实际需求,这时就需要自定义相机了

自定义相机

除非万不得已,千万不要自定义
除非万不得已,千万不要自定义
除非万不得已,千万不要自定义

为什么这样说,因为坑多呀

想要自定义相机主要用到两个类

Camera
SurfaceView

他们的关系你可以看成:

Android 相机学习_第1张图片
image

注意Android5.0 用Camera2 取代Camera1 ,这里Camera1已经可以满足大部分需求,如果要实现更复杂的功能,建议使用Camera2

下面我们就来自己开发一个自定义相机

  1. 添加相关权限
  2. 在相关xml中添加SurfaceView,用于显示预览界面
  3. 获取SurfaceHolder添加相关监听
  4. 在surfaceCreated回调处打开相机,设置相关参数,开启预览
  5. 拍照,获得图片
  6. 图片处理,保存
  7. 退出,释放资源

权限

  
  

在相关xml中添加SurfaceView,用于显示预览界面


         

获取SurfaceHolder添加相关监听

        mSurfaceView = findViewById(R.id.sv);
        mSurfaceHolder = mSurfaceView.getHolder();
        mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
        surfaceHolderCallBack = new SurfaceHolderCallBack();
        mSurfaceHolder.addCallback(surfaceHolderCallBack);
    
class SurfaceHolderCallBack implements SurfaceHolder.Callback {

        @Override
        public void surfaceCreated(final SurfaceHolder surfaceHolder) {
            new Thread(new Runnable() {//启动相机 特别耗时 需要开启线程处理 
                @Override
                public void run() {
                  //打开相机操作
                }
            }).start();

        }

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

        }

        @Override
        public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
         //释放相机资源
        }
    }

在surfaceCreated回调处打开相机,设置相关参数,开启预览

     //打开相机
     Camera camera = Camera.open(//相机Id);
     //设置预览界面
     camera.setPreviewDisplay(//surfaceHolder);
     //设置预览角度
     //camera.setDisplayOrientation(//角度);
     //开启预览
     camera.startPreview();

完成上述几步,就可以看见预览界面了,不过会发现你的预览结果和你想象的应该不太一样,第一个坑出现了,这里你只需要知道camera.setDisplayOrientation(//角度)是处理这个问题的,后面会对出现的坑做统一的解释

预览完成了,接下来解释拍照了

拍照,获得图片

我采用的自动变焦的方式拍照,大概流程是:点击拍照后,开始自动变焦,变焦完成,相机开始拍照,拍照完成获取图片

   /**
     * 照相
     */
    public void capture(Camera camera) {
        if (camera != null) {
            Camera.Parameters parameters = camera.getParameters();
            //TODO 判断是否支持自动变焦
            parameters.setFlashMode(Camera.Parameters.FOCUS_MODE_AUTO);
            //需要设置图片的尺寸
//            List supportedPictureSizes = parameters.getSupportedPictureSizes();
//            for (Camera.Size supportedPictureSize : supportedPictureSizes) {
//                if (supportedPictureSize.width == 1280) {//取景方向默认是横向的
//                    parameters.setPictureSize(supportedPictureSize.width, supportedPictureSize.height);
//                }
//            }
            //TODO 一定注意保存
            camera.setParameters(parameters);
            camera.autoFocus(new Camera.AutoFocusCallback() {
                @Override
                public void onAutoFocus(boolean success, Camera camera) {
                    if (success) {
                        camera.takePicture(null, null, pictureCallback);
                    }
                }
            });
        }
    }
    
    /**
     * 相机生成的图片数据
     */
    Camera.PictureCallback pictureCallback = new Camera.PictureCallback() {
        @Override
        public void onPictureTaken(byte[] bytes, Camera camera) {
            //TODO 可能需要一个线程去执行
            saveImage(bytes, "temp1.png");
        }
    };

恩,拍照获取图片也完成了,细心的朋友会返现上面有一段注释掉的代码,这是为了第二坑,至于坑是什么后面说.
获取到图片我们就可以保存了

图片处理,保存

 //保存图片
        File rootFile = new File(rootFolderPath + File.separator + "capture" + File.separator + "temp1.jpg");
        FileOutputStream os = null;
        try {
            os = new FileOutputStream(rootFile);
            bitmap2.compress(Bitmap.CompressFormat.JPEG, 100, os);
        } catch (Exception e) {
            e.printStackTrace();
        }

这样图片也保存了,看起来没啥问题,不过第三坑出现了,依旧后面解释.

最后

退出,释放资源

    ...
   @Override
        public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
         //释放相机资源
          if (camera != null) {
              camera.stopPreview();
              camera.release();
        }
        }
    ...

一个简单的相机就完成了,试着运行一下,结果貌似有些出入,这就需要我们来解决上面提到的三个坑了,坑是什么,怎么解决,请看下篇文章

你可能感兴趣的:(Android 相机学习)