Camera使用-实现简单的自定义相机

这篇文章总结使用Camera API实现自定义相机

之前文章 Android 拍照功能详解介绍了调用系统相机应用完成拍照功能。

但很多时候,应用需要自定义实现相机拍照功能,Android5.0以后推出了Camera2.0来替代之前的Camera API,但是很多时候我们还是需要使用Camera API来实现相机的自定义。


这里写图片描述

关键知识点

  1. SurfaceView的使用,相机预览界面是通过SurfaceView实现的;
  2. Camera API的使用;
  3. 拍照生成的文件的保存与读取。

权限申请
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-feature android:name="android.hardware.camera"/>

相机权限,文件存储权限。

相机界面
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <SurfaceView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/surfaceview"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="拍照"
        android:layout_gravity="center|bottom"
        android:onClick="takePhoto"/>

</FrameLayout>

这里为什么要使用SurfaceView,因为普通的View无法实现高效的拍照页面视图渲染。

Camera API使用
Camera初始化
 /**
     * 初始化相机对象
     * @return
     */
    private Camera initCamera(){

        Camera camera;
        try {
            camera = Camera.open();
        } catch (Exception e) {
            camera = null;
            e.printStackTrace();
        }

        return camera;
    }

通过Camera.open()方法得到Camera对象。

SurfaceHolder 初始化
 @Override
    protected void preInitData() {

        mFile = new File(getIntent().getStringExtra("file"));

        mHolder = mPreview.getHolder();
        mHolder.addCallback(this);
    }

SurfaceHolder是SurfaceView的关键方法,通过SurfaceView获取到SurfaceHolder,然后实现其回调方法。

SurfaceHolder的回调方法实现
 @Override
    public void surfaceCreated(SurfaceHolder holder) {
        startPreview(mCamera,holder);
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        //先关闭,再开启
        mCamera.stopPreview();
        startPreview(mCamera,holder);
    }

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

其中startPreview(mCamera,holder)是我实现的预览相机内容的方法,releaseCamera()方法是释放相机资源的方法。

预览相机内容
 /**
     * 开始预览相机内容
     */
    private void startPreview(Camera camera ,SurfaceHolder holder){
        try {
            camera.setPreviewDisplay(holder);
            //将Camera预览角度进行调整
            camera.setDisplayOrientation(90);
            camera.startPreview();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

通过Camera跟SurfaceHolder产生关联,就可以将Camera的预览内容展现在SurfaceView中。

释放相机资源
/**
     * 释放相机资源
     */
    private void releaseCamera(){
        if(mCamera != null){
            mCamera.setPreviewCallback(null);
            mCamera.stopPreview();
            mCamera.release();
            mCamera = null;
        }
    }
获得拍照生成的照片
/**
     * 拍照按钮点击事件
     * @param view
     */
    public void takePhoto(View view){
        setParameters();
        mCamera.autoFocus(new Camera.AutoFocusCallback() {
            @Override
            public void onAutoFocus(boolean success, Camera camera) {
                if(success){
                    //前两个参数可以为空,第三个方法是照片的回调
                    mCamera.takePicture(null,null,mPictureCallback);
                }
            }
        });
    }

我们这里拿到回调的字节流,然后生成文件保存,最后将文件数据放入Intent中,关闭当前页面。
其中mPictureCallback方法是相机拍照结果的回调方法:

private Camera.PictureCallback mPictureCallback = new Camera.PictureCallback() {
        @Override
        public void onPictureTaken(byte[] data, Camera camera) {
            try {
                FileOutputStream fos = new FileOutputStream(mFile);
                fos.write(data);
                fos.close();

                Intent intent = new Intent();
                intent.putExtra("file",mFile.getAbsolutePath());
                setResult(0,intent);
                finish();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    };

setParameters()方法是我们对Camera一些参数的设置:

/**
     * 开始预览相机内容
     */
    private void startPreview(Camera camera ,SurfaceHolder holder){
        try {
            camera.setPreviewDisplay(holder);
            //将Camera预览角度进行调整
            camera.setDisplayOrientation(90);
            camera.startPreview();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

关键的方法基本上都介绍完了,下面我将CameraActivity界面的所有代码都放出来,方便大家的动手操作。

public class Camera1Activity extends BaseActivity implements SurfaceHolder.Callback{

    @BindView(R.id.surfaceview)SurfaceView mPreview;

    private File mFile;
    private Camera mCamera;
    private SurfaceHolder mHolder;

    private Camera.PictureCallback mPictureCallback = new Camera.PictureCallback() {
        @Override
        public void onPictureTaken(byte[] data, Camera camera) {
            try {
                FileOutputStream fos = new FileOutputStream(mFile);
                fos.write(data);
                fos.close();

                Intent intent = new Intent();
                intent.putExtra("file",mFile.getAbsolutePath());
                setResult(0,intent);
                finish();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    };


    @Override
    protected int getContentViewResId() {
        return R.layout.camera1;
    }

    @Override
    protected void onResume() {
        super.onResume();

        if(mCamera == null){
            mCamera = initCamera();
            if(mHolder != null){
                startPreview(mCamera,mHolder);
            }
        }

    }

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

        releaseCamera();
    }

    @Override
    protected void preInitData() {

        mFile = new File(getIntent().getStringExtra("file"));

        mHolder = mPreview.getHolder();
        mHolder.addCallback(this);
    }

    @OnClick(R.id.surfaceview)
    public void onSurfaceviewClick(){
        mCamera.autoFocus(null);
    }

    /**
     * 拍照按钮点击事件
     * @param view
     */
    public void takePhoto(View view){
        setParameters();
        mCamera.autoFocus(new Camera.AutoFocusCallback() {
            @Override
            public void onAutoFocus(boolean success, Camera camera) {
                if(success){
                    //前两个参数可以为空,第三个方法是照片的回调
                    mCamera.takePicture(null,null,mPictureCallback);
                }
            }
        });
    }

    /**
     * 初始化相机对象
     * @return
     */
    private Camera initCamera(){

        Camera camera;
        try {
            camera = Camera.open();
        } catch (Exception e) {
            camera = null;
            e.printStackTrace();
        }

        return camera;
    }

    /**
     * 开始预览相机内容
     */
    private void startPreview(Camera camera ,SurfaceHolder holder){
        try {
            camera.setPreviewDisplay(holder);
            //将Camera预览角度进行调整
            camera.setDisplayOrientation(90);
            camera.startPreview();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 释放相机资源
     */
    private void releaseCamera(){
        if(mCamera != null){
            mCamera.setPreviewCallback(null);
            mCamera.stopPreview();
            mCamera.release();
            mCamera = null;
        }
    }

    /**
     * 设置相机参数
     */
    private void setParameters(){
        Camera.Parameters parameters = mCamera.getParameters();
        parameters.setPictureFormat(ImageFormat.JPEG);
        parameters.setPreviewSize(800,400);
        parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        startPreview(mCamera,holder);
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        //先关闭,再开启
        mCamera.stopPreview();
        startPreview(mCamera,holder);
    }

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

拍照功能实现了,接下来我们处理拍照后相片的显示操作。仔细阅读文章的同学会发现,我们在跳转至CameraActivity中的时候传入了照片文件的路径。

 public void photo(View view){
        Intent intent = new Intent(this,Camera1Activity.class);
        intent.putExtra("file",createImageFile("mFile").toString());
        startActivityForResult(intent,TAKE_PHOTO_CUSTOM);
    }

    private File createImageFile(String fileName){
        File dir = new File(getCacheDir(),"images");
        if(!dir.exists())
            dir.mkdirs();
        return new File(dir,fileName);
    }

同时,我们将拍照后的相片路径放入了Intent中。

@Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if(requestCode == TAKE_PHOTO_CUSTOM){
            if(data != null){
//                Bitmap resultBitmap = BitmapFactory.decodeFile(filePath);
//                mImageView.setImageBitmap(resultBitmap);
                try {
                    String filePath = data.getStringExtra("file");
                    FileInputStream fis = new FileInputStream(filePath);
                    Bitmap bitmap = BitmapFactory.decodeStream(fis);
                    Matrix matrix = new Matrix();
                    matrix.setRotate(90);
                    bitmap = Bitmap.createBitmap(bitmap,0,0,
                            bitmap.getWidth(),bitmap.getHeight(),matrix,true);
                    mImageView.setImageBitmap(bitmap);
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                }
            }
        }
    }

因为我直接将相片拿出来显示的话,照片显示效果是旋转了90度,至于为什么如此,仔细的同学可能会发现在相机Camera API使用的时候我们对Camera进行了90度的旋转,因为Camera默认相片是横屏显示的。因此我在OnActivityResult中对照片显示的imageview进行了90度的旋转,这样照片就可以正确显示在ImageView中。

Camera的使用简答介绍到这里,实现了简单拍照功能。至于项目中对自定义拍照功能的应用,相信在此基础上进行一定的完善就可以满足一般的功能需求。

感觉有帮助的同学欢迎留言交流,点赞关注!谢谢。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值