博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android 自定义Camera基本使用步骤及关键注意点
阅读量:6294 次
发布时间:2019-06-22

本文共 9320 字,大约阅读时间需要 31 分钟。

hot3.png

刚接触Camera这一块的开发,看了两天的API,终于搞懂了Camera的一些基本功能使用,编写博客记录一下,也分享些经验给同学们。

1. Camera的生命周期

    open()---->startPreview()---------------------->stopPreview()---->release()--------->{open()}[再次使用]

/**     * 开启Camera     * @param cameraType     * @return     */    public Camera openCamera(Consts.CameraType cameraType){        Camera mCamera = null;        int cameraId = getCameraId(cameraType);        mCamera = Camera.open(cameraId);        Log.i(TAG,"camera open");        return mCamera;    }    /**     * 开启Camera的预览     * @param camera     */    public void startCameraPreview(Camera camera){        if (camera==null||isCameraPreviewStart){            return;        }        camera.startPreview();        Log.i(TAG,"camera start");        isCameraPreviewStart = true;    }    /**     * 结束Camera的预览并释放     * @param camera     */    public void stopCameraPreview(Camera camera){        if (camera==null||!isCameraPreviewStart){            return;        }        camera.stopPreview();        camera.release();        Log.i(TAG,"camera stop");        isCameraPreviewStart = false;    }

   我在这里对Camera的一些基本方法进行了封装,这样就方便了下次的使用,减少代码的重复量。

    使用步骤:

  1. Obtain an instance of Camera from .
  2. Get existing (default) settings with .
  3. If necessary, modify the returned  object and call .
  4. If desired, call .
  5. Important: Pass a fully initialized  to . Without a surface, the camera will be unable to start the preview.
  6. Important: Call  to start updating the preview surface. Preview must be started before you can take a picture.
  7. When you want, call  to capture a photo. Wait for the callbacks to provide the actual image data.
  8. After taking a picture, preview display will have stopped. To take more photos, call  again first.
  9. Call  to stop updating the preview surface.
  10. Important: Call  to release the camera for use by other applications. Applications should release the camera immediately in  (and re- it in ). 

   以上是android.hardware.camera的api使用步骤。(英语水平不高的同学们就百度翻译一下再看啦O(∩_∩)O哈!~我就是翻译后看的~)

  注意:

     1.在调用Camera的open()方法之前,程序员必须要对使用的手机设备进行检测,判断该设备是否拥有相机这一功能特征:如果没有的话,则直接return,结束此程序;如果有的话,再调用open()方法。(一般的智能机都有相机的) 这里附上判断的代码--使用hasSystemFeature的方法检测

public boolean checkDevicesHasCamera(Context context){        return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)         || context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT);}

    2.open()方法在API中有两个:open() 和open(int cameraId)  第一次无参数的不用多说,关键是第二个open(int cameraId),这里的cameraId表示相机的id,为什么相机还有id呢?这是因为有些手机不仅仅只有一个摄像头,其包括了前置摄像头和后置摄像头(还有些手机不止这两个)。因此,系统需要根据你传给它的cameraId来open前置还是后置摄像头,也就是说,你还需要获取前置/后置摄像头的cameraId来帮助你实现open相应设备。这里附上获取cameraId的代码(CameraType是我写的一个枚举变量,里面有CameraBack和CameraFront,分别标志前置和后置)

private int getCameraId(Consts.CameraType cameraType){        int cameraId = -1,back_id=-1,front_id=-1;        int counts = Camera.getNumberOfCameras();        for (int i = 0; i < counts; i++) {            Camera.CameraInfo  cameraInfo= new Camera.CameraInfo();            Camera.getCameraInfo(i,cameraInfo);            if (cameraInfo.facing==Camera.CameraInfo.CAMERA_FACING_BACK) back_id = i;            if (cameraInfo.facing==Camera.CameraInfo.CAMERA_FACING_FRONT) front_id = i;        }        cameraId = (cameraType== Consts.CameraType.CameraBack)?back_id:front_id;        return cameraId;}

    3.使用Camera,我们不可能只是预览界面而已,可能还有很多操作:比如说拍照/聚焦/开启闪光灯等等,而这些功能的实现,都必须是在startPreview()-----------------------stopPreview()之间执行的。[关键!]

2.使用Camera

    2.1 创建相机预览窗口

           在生命周期中我们看到需要调用startPreview(),也就是启动预览,那么预览的东西在哪里呢?这是需要你自己亲手创建的!这里教大家两种方法:

            方法一:自定义view 继承与surfaceView 并实现其SurfaceHolder.CallBack接口,然后将该view

添加到你创建的预览布局中(这个不用解释吧,view只有放置在布局里面,才能关联到activity中去)。这里附上代码

public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback{    public SurfaceHolder mHolder;    private Camera mCamera;    //创建一个surface来preview camera    public CameraPreview(Context context,Camera camera) {        super(context);        mHolder = this.getHolder();        mCamera = camera;        mHolder.addCallback(this);        mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);    }    public SurfaceHolder getSurfaceHolder(){        if (mHolder!=null){            return mHolder;        }else {            return null;        }    }    @Override    public void surfaceCreated(SurfaceHolder surfaceHolder) {        try {            mCamera.setPreviewDisplay(mHolder);            mCamera.startPreview();//启动preview        } catch (IOException e) {            e.printStackTrace();        }    }    @Override    public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {        if (mHolder==null){            return;        }        //当surface发生改变的时候,先关闭掉preview,然后再次启动        mCamera.stopPreview();        try {            mCamera.setPreviewDisplay(mHolder);            mCamera.startPreview();//启动preview        } catch (IOException e) {            e.printStackTrace();        }    }    @Override    public void surfaceDestroyed(SurfaceHolder surfaceHolder) {        System.out.println("surfaceDestroyed");    }}

            方法二: 在布局文件中直接使用TextureView然后为TextureView设置SurfaceTextureListener监听事件最后使用Camera.setPreviewTexture(TextureView.getSurfaceTexture)即可 【推荐方法】 这里附上代码

private TextureView.SurfaceTextureListener mSurfaceListener = new TextureView.SurfaceTextureListener() {        @Override        public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int i, int i1) {            mSurfaceTexture = surfaceTexture;            cameraUtils.setCameraParameter(camera,cameraType);            cameraUtils.setCameraPreviewSize(camera,screenWidth,screenHeight);            try {                camera.setPreviewTexture(mSurfaceTexture);                cameraUtils.startCameraPreview(camera);            } catch (IOException e) {                e.printStackTrace();            }        }        @Override        public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int i, int i1) {        }        @Override        public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {            cameraUtils.stopCameraPreview(camera);            return true;        }        @Override        public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {        }};textureView = (TextureView) findViewById(R.id.textureView);textureView.setSurfaceTextureListener(mSurfaceListener);

            注意:

                1.在cameraUtils.setCameraParameter(camera,cameraType)方法中,需要将camera的预览显示状况进行顺时针的旋转90度,因为在Camera中默认显示的是横屏的画面,就是将画面逆时针旋转了90度,因此我们在startPreview之前,要将该画面顺时针转回来。

                 2.cameraUtils.setCameraPreviewSize(camera,screenWidth,screenHeight)方法的目的是避免预览界面出现“失真”----拉伸 状况。这是一个关键点,这个大家可以下载我后面给出的链接查看,也可以等我下一次发布博客进行详解。

    2.2 进行拍照保存功能

            在启动了预览功能之后,即startPreview()之后,stopPreview()之前,我们可以执行一系列的相关操作(这个问题我强调了很多遍,大家必须要记住,千万不要犯那个stopPreview了或者release()了之后,还对camera进行操作)。 而拍照功能,camera API中提到了一个方法 takePicture(),我们实现这个方法即可实现拍照功能。

             步骤如下:

  • 首先需要明确我们拍照所得到的图片保存到本地的什么位置,即本地保存目录。这里附上代码
public static File getOutputImageFile(){        File mediaStorageDir = new File(Environment.getExternalStorageDirectory().getAbsolutePath()+"/Pictures");        if (!mediaStorageDir.exists()){            if (!mediaStorageDir.mkdir()){                System.out.println("创建文件夹失败");            }        }        // Create a media file name        String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());        File mediaFile = new File(mediaStorageDir.getPath() + File.separator +                "IMG_"+ timeStamp + ".jpg");        System.out.println(mediaFile.getAbsolutePath());        return mediaFile;}
  • 接下来就是实现takePicture方法了
camera.takePicture(null, null, null, new Camera.PictureCallback() {            @Override            public void onPictureTaken(final byte[] bytes, final Camera camera) {                final File file = BaseUtils.getOutputImageFile();                final String filePath = file.getPath();                new Thread(new Runnable() {                    FileOutputStream fops = null;                    @Override                    public void run() {                        try {                            fops = new FileOutputStream(filePath);                            fops.write(bytes);                        } catch (FileNotFoundException e) {                            e.printStackTrace();                        } catch (IOException e) {                            e.printStackTrace();                        } finally {                            if (fops!=null){                                try {                                    fops.close();                                } catch (IOException e) {                                    e.printStackTrace();                                }                            }                        }                    }                }).start();            }});
  • 在takePicture之后,系统会在内容调用stopPreview方法,即介绍了这一次的camera操作,但是我们不可能一次启动这个应用只拍一张照片就结束了的,因此需要重新启动camera
private void restartCamera() {        cameraUtils.stopCameraPreview(camera);        camera = cameraUtils.openCamera(cameraType);        try {            camera.setPreviewTexture(mSurfaceTexture);        } catch (IOException e) {            e.printStackTrace();        }        cameraUtils.setCameraParameter(camera,cameraType);        cameraUtils.startCameraPreview(camera);}

到这里,一个基本的自己创建的相机功能已经基本完成了!希望大家能够写出属于自己的Camera应用,而不是简单地调用系统的相机!

这里附上本人的Demo链接 http://git.oschina.net/meinkonone/CameraTest

转载于:https://my.oschina.net/mkonone/blog/793153

你可能感兴趣的文章
Laravel5.4重新登陆跳转到登陆前页面的原理和实现
查看>>
Chrome Full black Screen [Solved]
查看>>
Android动态权限管理模型(4.3-6.0)
查看>>
UI仿写 - 收藏集 - 掘金
查看>>
svg做自定义折线图表
查看>>
Koa源码分析(二) -- co的实现
查看>>
nohup和&的区别与关系
查看>>
UE4链接第三方库(lib和dll)
查看>>
phpstrom中让volt高亮显示
查看>>
macOS下nginx配合obs做推流直播.md
查看>>
数据结构——树
查看>>
浅析React之事件系统(二)
查看>>
Elixir 1.2带来多项功能增强和性能提升
查看>>
IPv6新形势下的安全解决方案
查看>>
红帽论坛北京站召开 设立亚太开放创新实验室
查看>>
函数式编程语言时代已经来临
查看>>
微软云数据库 Azure SQL DB Hyperscale如何实现超大规模存储和高可用?
查看>>
十年中文技术社区风雨之路 今晚4位老炮畅聊过去未来
查看>>
微软发起Java on Azure调查,呼吁Java社区积极参与
查看>>
SignalR Core尝鲜
查看>>