Android Camera相机基础使用

前言

拍照功能在智能机中已经是标配了,市场上也充斥着形形色色的相机应用,足以说明相机功能目前的火爆程度。作为Android开发者拍照相关的API也是程序员必备的技能,现在就来学习一下Android系统为开发者提供的拍照相关的技术。

6.0+权限申请

由于相机权限和SDCard外部存储权限到了6.0+版本都属于危险权限,需要用户手动打开权限才能使用相机和保存照片到外部存储。在6.0+系统使用相机通常都要添加动态权限申请的逻辑,首先要在AndroidManifest文件中声明权限。

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

然后在Activity里需要申请权限的地方添加权限是否申请和没有权限申请权限的功能:

 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    if (ContextCompat.checkSelfPermission(IndexActivity.this,
            Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED &&
            ContextCompat.checkSelfPermission(IndexActivity.this,
                    Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
        // 如果已经申请到权限直接做操作
    } else {
        // 没有权限申请权限
        ActivityCompat.requestPermissions(IndexActivity.this,
                new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.CAMERA }, REQUEST_WRITE_STORAGE);
    }
}

用户点击权限申请的按钮后会产生回调事件,在权限回调中检查用户是否赋予权限,赋予了拍照权限之后才可以继续操作。

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if (requestCode == REQUEST_WRITE_STORAGE && grantResults != null && grantResults.length > 0) {
        if (grantResults[0] == PackageManager.PERMISSION_GRANTED && grantResults[1] == PackageManager.PERMISSION_GRANTED) {
            // 如果相机和外部存储都已经授权
        }
    }
}

系统相机

打开系统相机只需要简单的设置Intent的action为MediaStore.ACTION_IMAGE_CAPTURE,如果用户需要接收系统相机拍照得到的照片数据就要使用startActivityResult调起系统相机在onActivityResult里获取照片数据。

public void startCamera() {
    Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    startActivityForResult(intent, REQUEST_TAKE_PHOTO);
}

   @Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == REQUEST_TAKE_PHOTO && resultCode == Activity.RESULT_OK) {
        Bundle bundle = data.getExtras();
        // 取出的数据其实是缩略图
        Bitmap bitmap = (Bitmap) bundle.get("data");
        imageView.setImageBitmap(bitmap);
}

现在智能机屏幕越来越大,拍摄的照片清晰度也越来越高,但是Android的Intent最大只能方1M的数据,超过就会抛出异常,所以Intent里取出来的照片只是真实照片的缩略图。如果希望得到真实的照片数据,需要向系统相机传递接收照片的本地图片文件Uri。

public void startCamera2() {
    Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(filePath)));
    startActivityForResult(intent, REQUEST_TAKE_PHOTO2);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
     if (requestCode == REQUEST_TAKE_PHOTO2 && resultCode == Activity.RESULT_OK) {
        Bitmap bitmap = BitmapFactory.decodeFile(filePath);
        imageView.setImageBitmap(bitmap);
    }
}

自定义拍照

系统相机使用非常简单明了,但是交互方式太过于单一,界面也很粗糙,难以满足高级用户的需求。开发者通过自定义拍照功能可以很好的提高用户体验,实现更加人性化的拍照效果。

相机捕获的数据量非常大,使用普通的View控件根本无法高效的展示实时图片;SurfaceView能用来承担这项任务,不过需要在它的生命周期里打开和关闭相机,防止出现意外情况。Camera本身是硬件服务,如果有用户申请到了就会以独占的方式使用,只要用户为释放Camera就会导致其他用户永远无法申请到Camera,用户需要处理Camera的申请和释放工作。

takePicture = (Button) findViewById(R.id.takePicture);
surfaceView = (SurfaceView) findViewById(R.id.surfaceView);
holder = surfaceView.getHolder();
holder.addCallback(this);

// 初始化Camera对象
private Camera getCamera() {
    Camera camera;
    try {
        camera = Camera.open();
    } catch (Exception e) {
        camera = null;
        e.printStackTrace();
    }

    return camera;
}

// 开始相机预览功能
private void setStartPreview(Camera camera, SurfaceHolder holder) {
    try {
        camera.setPreviewDisplay(holder);
        // 旋转角度使之竖屏展示
        camera.setDisplayOrientation(90);
        camera.startPreview();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

// 释放申请到的相机
private void releaseCamera() {
    if (camera != null) {
        camera.setPreviewCallback(null);
        camera.stopPreview();
        camera.release();
        camera = null;
    }
}

在Activity退到后台就不需要相机捕获数据,这时需要释放相机,Activity进入前台这时可以请求相机并且开始预览效果。当然SurfaceView的Surface创建时可以开始预览效果,如果Surface改变就需要先暂停Camera再重新开始预览功能。

@Override
protected void onPause() {
    super.onPause();
    releaseCamera();
}

@Override
protected void onResume() {
    super.onResume();
    if (camera == null) {
        camera = getCamera();
        if (holder != null) {
            setStartPreview(camera, holder);
        }
    }

}

@Override
public void surfaceCreated(SurfaceHolder holder) {
    setStartPreview(camera, holder);
}

@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
    camera.stopPreview();
    setStartPreview(camera, holder);
}

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
    releaseCamera();
}

定义好了Camera对象和SurfaceView生命周期交互,就可以开始真实的拍照了,camera.takePhoto();方法实现了拍照功能而且提供了图片回调,在回调内部可以获取到照片Jpg数据。

private void capture() {
    // 设置相机的参数
    Camera.Parameters parameters = camera.getParameters();
    // 相机图片格式为jpeg
    parameters.setPictureFormat(PixelFormat.JPEG);
    // 初始大小
    parameters.setPreviewSize(800, 400);
    // 聚焦模式
    parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
    // 开始拍照
    camera.takePicture(null, null, new Camera.PictureCallback() {
        // 获取拍照返回的jpg图片数据
        @Override
        public void onPictureTaken(byte[] data, Camera camera) {
            File tmpFile = new File("/sdcard/temp.png");
            try {
                FileOutputStream fos = new FileOutputStream(tmpFile);
                fos.write(data);
                fos.close();

                // 将图片放到另外一个Activity中展示
                Intent intent = new Intent(CustomCameraActivity.this, ResultActivity.class);
                intent.putExtra("filePath", tmpFile.getAbsolutePath());
                startActivity(intent);
                finish();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    });
}

Camera功能强大实现简单,不过从5.0之后系统废弃了这套逻辑开始使用Camera2来实现拍照功能,后面将会使用Camera2接口重新定制一个简单的相机功能。

猜你喜欢

转载自blog.csdn.net/xingzhong128/article/details/80016251