美文网首页
摄像头的使用之(Camera的使用)

摄像头的使用之(Camera的使用)

作者: 笔墨Android | 来源:发表于2018-01-19 14:49 被阅读0次

不怕跌倒,所以飞翔

最近观看视频学习的时候,看见慕课网上又关于Camera实现自定义拍照的功能,特此写下笔记记录一下!

本文知识点

  • Android实现基本的拍照功能
  • Camera结合SurfaceView实现简单的自定义拍照功能

Android实现基本的拍照功能

在Android中实现最基本的拍照功能,无非就是调用系统的相机,并成功获取到相应拍摄的照片显示到指定的位置.这里代码描述一下:

1.跳转到系统相机页面

        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        Uri photoUri = Uri.fromFile(new File(mPath));
        intent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri);
        startActivityForResult(intent, 1);

这里说明几点问题:

  1. 首先Action是指定的,这里指定的是去系统相机页面;
  2. 你可以指定照片保存的相应路径,也就是第二行的URI的内容,将路径转成相应的URI传入到系统相机页面,照相完成后就保存到你指定的页面了,为社么这里要指定路径呢?因为如果你没有指定路径的话,你从onActivityResult()中返回的Intent中的"data"中获取到相应的图片数据,这里可以直接强转成Bitmap对象,但是这个数据返回的图片是压缩了的(很不清楚).但是指定了相应的路径之后,你就可以直接从相应的路径去获取图片,这样获取到的图片就很清楚了.

2.获取相应的图片

这里获取图片分为两种:

  • 没有指定图片的路径的获取方法(但是这里取出来的是一张缩略图,不是很清楚)

            Bundle bundle = data.getExtras();
            /*这里取出的是一张缩略图*/
            Bitmap bitmap = (Bitmap) bundle.get("data");
            mIv.setImageBitmap(bitmap);//设置图片的方法
    
  • 指定了图片的路径的获取方法(这里其实就是相应的文件读写了)

            FileInputStream fis = null;
            try {
                fis = new FileInputStream(mPath);
                Bitmap bitmap = BitmapFactory.decodeStream(fis);
                mIv.setImageBitmap(bitmap);//设置图片的方法
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } finally {
                if (fis != null) {
                    try {
                        fis.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
    

通过以上方式就能获取相应的图片进行设置了.

Camera结合SurfaceView实现简单的自定义拍照功能

其实这个功能最主要的是Camera和SurfaceView控件的结合,其次就是Camera给我们提供了相当丰富的API,可以让我们在只调用相应API的情况下就能实现拍照功能,但是要考虑的细节还是很多的,后面我也会相应的讲解.

1.布局文件创建相应的SurfaceView提供相应的预览页面

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.jinlong.kehai.CustomCameraActivity">

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

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="20dp"
        android:onClick="StartCamera"
        android:text="拍照"
        android:id="@+id/button"/>
</RelativeLayout>

这里其实就创建了一个SurfaceView的全屏布局,然后创建了一个相应的拍照按钮,没有什么可说的.

2.创建相应的Camera并控制其生命周期

关于Camera的生命周期的问题很重要,为什么这么说呢?设想一下,当你按Home键退出的时候,如果你不对相应的生命周期进行处理,可能在返回的时候就会出现相应的问题.所以这里说相应的生命周期是很重要的问题.

2.1获取相应的Camera对象

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

Camera对象是通过open()方法进行创建的.

2.2获取相应的SufraceView的对象

在Camera有提供预览的方法,但是这个方法需要一个相应的SurfaceHolder对象,所以我们必须通过SurfaceView中获取到相应的SurfaceHolder对象,这里就很简单了.看下面的具体代码

        SurfaceView mSurfaceView = (SurfaceView) findViewById(R.id.sv);
        mSurfaceHolder = mSurfaceView.getHolder();
        mSurfaceHolder.addCallback(this);

这里面的callBack是SurfaceHolder相应的回调.

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        /*创建*/
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        /*改变 */
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        /*销毁*/
    }

相应的内容我已经写在注释中了,这里其实在相应的方法中也应该关注Camera的生命周期处理.然后代码边长这个样子

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        Log.e(TAG, "surfaceCreated: ");
        /*创建*/
        setStartPreview(mCamera, holder);
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        Log.e(TAG, "surfaceChanged: ");
        /*改变,重启整个预览功能*/
        mCamera.stopPreview();
        setStartPreview(mCamera, holder);
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        Log.e(TAG, "surfaceDestroyed: ");
        /*销毁*/
        releaseCamera();
    }

这里用到的相应的方法,会在后面提供.

2.3Camera生命周期的处理

其实关于生命周期的处理无非就是在相应Activity失去焦点的时候结束掉相应的Camera,然后在Activity获取焦点的时候创建Camera.其次就是在surfaceHolder回调中处理一下相应的生命周期就可以了.这里我选择的是在相应的onResume()中去启动相应的Camera.然后在onPause()中释放相应的资源.相应的代码如下:

    @Override
    protected void onResume() {
        super.onResume();
        if (mCamera == null) {
            mCamera = getCamera();
            if (mSurfaceHolder != null) {
                Log.e(TAG, "onResume: ");
                setStartPreview(mCamera, mSurfaceHolder);
            }
        }
    }

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

    /*设置预览内容,开始预览*/
    public void setStartPreview(Camera camera, SurfaceHolder holder) {
        /*camera和SurfaceHolder进行关联*/
        try {
            camera.setPreviewDisplay(holder);
            /*系统默认camera是横屏的*/
            /*设置角度 TODO 这里很重要的*/
            camera.setDisplayOrientation(90);
            camera.startPreview();
            Log.e(TAG, "setStartPreview: ");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /*释放资源*/
    private void releaseCamera() {
        if (mCamera != null) {
            mCamera.setPreviewCallback(null);/*这个是预览的回调,里面会返回一个Byte[]和相应的Camera*/
            mCamera.stopPreview();/*取消预览功能*/
            mCamera.release();
            mCamera = null;
        }
    }

仔细讲解一下上面的内容,这些内容也算是本节的重点了.

  • 首先在onResume中我创建了一个相应的Camera,通过自己的方法setStartPreview()开启了预览
  • setStartPreview()中通过Camera的staetPreview()开启了预览, 这里面的holder是相应的SurfaceHolder,让SurfaceView提供相应的预览,这里其实有一个很意思的事情,就是你要是不设置相应的SurfaceHolder的话,拍照是可以进行的,而且还会有相应的图像,但是你不会看见相应的预览内容.
  • 这里还有一个问题很重要,预览默认是横屏的,所以这里应该设置预览是垂直于屏幕的.通过setDisplayOrientation();设置成90度就能保证预览是正确的内容.
  • Camera.setPreviewCallback这个是相应的预览时候回调的方法,这里会一直回调,这里面会回调一个相应的Byte[]和相应的Camera对象

通过上面的内容,你还是不能成功的预览,为什么呢?权限这个问题当时困扰我很久,

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

这样就可以完成相应的预览了,是不是很神奇.后来我仔细想了想,其实就是Camera获取到相应的数据,然后通过SurfaceView进行显示罢了,感觉就应该是这样,感觉一些相机的应用添加美颜的话,应该就是在相应的预览的哪个回调中添加了,但是具体还没有去尝试,但是我觉得应该是这....

接下来我们说说如何保存相应的拍照的图片.

2.4保存相应的图片

当你点击相应的拍照按钮的时候其实就是处理相应的保存操作的,这里一般都是通过保存到指定的路径来保存相应的图片.具体逻辑说明一下:

  • 设置相应的参数
  • 当在对焦完成的时候保存图片
  • 修正图片的角度
2.4.1 设置相应的参数

设置参数其实是通过Camera中的一个静态内部类(Parameters)完成的

        /*参数的一些设置*/
        Camera.Parameters parameters = mCamera.getParameters();
        /*格式*/
        parameters.setPictureFormat(ImageFormat.JPEG);
        /*大小*/
        parameters.setPreviewSize(800, 400);
        /*对焦,这里设置的是自动对焦*/
        parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);

这个静态内部类能设置很多参数,具体内容我也不太清楚,感觉查查API应该可以解决,所以这里就不深入研究了,等到有时间的时候在好好研究把!

2.4.2当对焦完成的时候进行保存图片

其实这里就是一个相应的对焦回调,然后直接保存图片就行了,这里千万别忘了添加写入SD卡的权限,没有权限是不行的!!!

        /*对焦最准确的时候的回调*/
        mCamera.autoFocus(new Camera.AutoFocusCallback() {
            @Override
            public void onAutoFocus(boolean success, Camera camera) {
                if (success) {
                    /*最后一个回调比较重要*/
                    mCamera.takePicture(null, null, mPictureCallback);
                }
            }
        });

autoFocus是一个相应的回调,这个回调只有一个方法,其实这里面就是一个boolean的参数和相机的对象,boolean的参数你返回成功的时候,你可以理解为对焦完成了,然后调用Camera的takePicture()完成拍照,但是这里面有相应的三个参数,其实这三个参数的含义大概是这样的:

  • 参数一:应该是从按下快门到捕获前这段时间,这里可以处理快门的声音提示,告知用户已经捕获完成了
  • 参数二:生成的最原始的图片的回调
  • 参数三:生成JPEG图片的回调

所以这里我们只是处理了生成JPEG的图片的回调,这个回调才是实际保存图片的地方,代码是这样的:

    private Camera.PictureCallback mPictureCallback = new Camera.PictureCallback() {
        @Override
        public void onPictureTaken(byte[] data, Camera camera) {
            /*data存储了整个图片的数据
            这里应该保存到指定的路径中去*/
            File tempFile = new File(Environment.getExternalStorageDirectory().getPath() + "/image.png");
            FileOutputStream fos = null;
            try {
                fos = new FileOutputStream(tempFile);
                fos.write(data);

                /*保存指定路径,之后就是传递值的问题了,这里是保存了相应的内容跳转到一个Activity*/
                Intent intent = new Intent(CustomCameraActivity.this, ResultActivity.class);
                intent.putExtra("path", tempFile.getPath());
                startActivity(intent);
                finish();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (fos != null) {
                    try {
                        fos.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    };

这里其实就是把相应的byte[]的内容写到相应的文件中去,有一段内容是跳转到相应的预览页面的.这里只是传过去一个相应的图片的位置,这里关于图片角度的处理就不去写了,不在本文讲述的内容中,这里只是把代码贴出来,感兴趣的同学自己去研究把!

            /*调整图片的角度*/
            File file = new File(path);
            FileInputStream fis = null;
            try {
                fis = new FileInputStream(file);
                Bitmap bitmap = BitmapFactory.decodeStream(fis);

                Matrix matrix = new Matrix();/*添加矩阵*/
                matrix.setRotate(90);
                bitmap = Bitmap.createBitmap(bitmap, 0, 0,
                        bitmap.getWidth(), bitmap.getHeight(), matrix, true);


                iv.setImageBitmap(bitmap);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } finally {
                if (fis != null) {
                    try {
                        fis.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }

以上内容就是简单的定义了一个拍照的页面,其实还有很多细节需要进行优化,这里没有讲的那么细致,因为我只是感兴趣,所以才研究了一下,毕竟我们公司不是做拍照的,所以有什么问题希望大家提出来,我在去研究,这样都能相互进步!如果有什么写的不对的,也希望您能提出来!感谢您的宝贵意见....

参考文献:
废墟的树关于Camera的文章
dongweiq的Camera详解

相关文章

  • 摄像头的使用之(Camera的使用)

    不怕跌倒,所以飞翔 最近观看视频学习的时候,看见慕课网上又关于Camera实现自定义拍照的功能,特此写下笔记记录一...

  • H5+

    camera Camera模块管理设备的摄像头,可用于拍照、摄像操作,通过plus.camera获取摄像头管理对象...

  • 待续

    Android Camera(摄像头) Android Camera Api的心得 Android从Camera中...

  • android-camera2相机开发【5】-获取、处理预览帧数

    camera2 api 中使用 ImageReader 类间接获取预览帧数据。 ImageReader 使用之前,...

  • 打开摄像头

    前几日一体机上测试摄像头功能是否正确,需要打开摄像头。代码使用Camera.open()打开摄像头,然后通过Tex...

  • Camera api2 frameworks

    Camera 工作结构 Camera Device:代表一个摄像头,在一部手机上有可能有多个摄像头,如前置摄像头,...

  • 解决UVCCamera项目报错:could not open c

    在使用UVCCamera时,打开外部摄像头时崩溃并抛出异常:could not open camera:err=-...

  • Cordova App iOS 摄像头打不开

    Cordova App iOS 摄像头打不开问题解决 使用 cordova-plugin-camera 添加插件 ...

  • android camera

    Android 7.0 Camera架构源码分析 高通camera结构(摄像头基础介绍) 智能手机双摄像头产业链、...

  • Android 使用mediaRecorder 录像时, onP

    我在使用Android camera录像同时取摄像头帧数据进行算法处理时,其中使用到了mediaRecorder进...

网友评论

      本文标题:摄像头的使用之(Camera的使用)

      本文链接:https://www.haomeiwen.com/subject/zywooxtx.html