zoukankan      html  css  js  c++  java
  • CameraAPI中的 自定义照相功能

    前几天的项目需要使用CameraAPI自己定义照相机,之前用过的二维码也要自己写底层代码,于是总结一下使用CameraAPI的几点事项。现在由于JDK7.0及其以上版本的官方文档已经不再推荐使用camera包而是camera2包,但这次还是先讲camera的使用,至于camera2等以后再讲。

    首先是添加照相机权限,在清单文件中必须添加摄像头硬件权限和使用功能,其中功能可以根据项目需求选择性放入。

     1     <uses-permission android:name="android.permission.CAMERA"/>
     2     <!--使用摄像头硬件功能-->
     3     <uses-feature android:name="android.hardware.camera"/>
     4     <!--自动对焦功能-->
     5     <uses-feature android:name="android.hardware.camera.autofocus"/>
     6     <!--闪光灯功能-->
     7     <uses-feature android:name="android.hardware.camera.flash"/>
     8     <!--前置摄像头-->
     9     <uses-feature android:name="android.hardware.camera.front"/>

    关于权限和对应的功能可以参考文章 http://www.cnblogs.com/BobGo/articles/5646751.html;

    我在项目中需要两个功能,一是在显示摄像头的SurfaceView上添加一层ImageView,可以在ImageView上绘制不同的遮罩层;还有一个功能是摄像头拍摄照片之后显示及保存图片。

    第一是摄像头简单的使用,在这里可能会遇到在SurfaceView中摄像头的预览出现变形问题,下面会提到解决措施:

    1         SurfaceHolder surfaceHolder = surfaceView.getHolder();
    2         surfaceHolder.addCallback(this);
    3         surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

    在当前类中实现SurfaceHolder.Callback 接口,重写三个方法 surfaceCreated(), surfaceChanged(), surfaceDestroy(),这三个方法就是SurfaceView对应的生命周期;

    surfaceCreated()中执行Camera.open()返回一个Camera对象,打开摄像头硬件,在surfaceDestroy()中,Camera对象调用release()释放摄像头;在surfaceChanged()中,设置摄像头参数,其中getBestSize()是确定手机摄像头硬件可以使用的最合适大小,这个算法是官方提供的。我在之前不采用这个算法而直接设置大小,导致在测试机运行时SurfaceView显示的图片出现变形。

     1 @Override
     2     public void surfaceCreated(SurfaceHolder holder) {
     3         camera = Camera.open();
     4     }
     5 
     6     @Override
     7     public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
     8         //已经获得Surface的width和height,设置Camera的参数
     9         Camera.Parameters parameters = camera.getParameters();
    10         Camera.Size size=getBestSize(parameters.getSupportedPreviewSizes());
    11         int w=size.width;
    12         int h=size.height;
    13         parameters.setPreviewSize(w, h);
    14         parameters.setPictureSize(w, h);
    15 //        List<Camera.Size> vSizeList = parameters.getSupportedPictureSizes();
    16 //        for (int num = 0; num < vSizeList.size(); num++) {
    17 //            Camera.Size vSize = vSizeList.get(num);
    18 //        }
    19 //        if (t?his.getResources().getConfiguration().orientation != Configuration.ORIENTATION_LANDSCAPE) {
    20             //强制竖屏模式
    21             parameters.set("orientation", "portrait");
    22             //在2.2以上可以使用,预览显示旋转90
    23 //        parameters.setRotation(90);
    24             camera.setDisplayOrientation(90);
    25 //        } else {
    26 //            parameters.set("orientation", "landscape");
    27             //在2.2以上可以使用
    28 //            camera.setDisplayOrientation(90);
    29 //        }
    30         camera.setParameters(parameters);
    31         try {
    32             //设置显示
    33             camera.setPreviewDisplay(holder);
    34         } catch (IOException exception) {
    35             camera.release();
    36             camera = null;
    37         }
    38         //开始预览
    39         camera.startPreview();
    40         //设置自动对焦
    41         camera.autoFocus(new Camera.AutoFocusCallback() {
    42             @Override
    43             public void onAutoFocus(boolean success, Camera camera) {
    44                 if (success) {
    45                     // success为true表示对焦成功,改变对焦状态图像
    46                 }
    47             }
    48         });
    49     }
    50 
    51     private Camera.Size getBestSize(List<Camera.Size> supportedPreviewSizes) {
    52         Camera.Size largestSize=supportedPreviewSizes.get(0);
    53         int largestArea= supportedPreviewSizes.get(0).height*supportedPreviewSizes.get(0).width;
    54         for (Camera.Size s:supportedPreviewSizes){
    55             int area=s.width*s.height;
    56             if(area>largestArea){
    57                 largestArea=area;
    58                 largestSize=s;
    59             }
    60         }
    61         return largestSize;
    62 
    63     }
    64 
    65     @Override
    66     public void surfaceDestroyed(SurfaceHolder holder) {
    67         // 释放手机摄像头
    68         camera.release();
    69     }

     也可以在surfaceChanged()中设置闪光灯等功能:

    1 parameters.setFlashMode(isChecked?Camera.Parameters.FLASH_MODE_ON:Camera.Parameters.FLASH_MODE_OFF);

    最后在需要摄像的点击监听中调用takePicture(Camera.ShutterCallback, Camera.PictureCallback, Camera.PictureCallback, Camera.PictureCallback)函数来完成拍照,这个函数中可以四个回调接口,ShutterCallback是快门按下的回调,在这里我们可以设置播放“咔嚓”声之类的操作,后面有三个PictureCallback接口,分别对应三份图像数据:原始图像、缩放和压缩图像和JPG图像,图像数据可以在PictureCallback接口的void onPictureTaken(byte[] data, Camera camera)中获得,三份数据相应的三个回调正好按照参数顺序调用,通常我们只关心JPG图像数据,此时前面两个PictureCallback接口参数可以直接传null

    每次调用takePicture() 获取图像后,摄像头会停止预览,假如需要继续拍照,则我们需要在上面的PictureCallbackonPictureTaken() 函数末尾,Camera对象再次调用startPreview() 函数;在不需要拍照的时候,Camera对象需要主动调用stopPreview() 停止预览功能;

    第二个功能是摄像图片的显示和保存,在这个功能中可能会出现两个问题,一个是拍摄图片横屏显示,另一个是将上边SurfaceView上方的ImageView遮罩层一同保存到用户手机,实现类似于图像合成的效果;

    关于图片横屏显示的问题,是因为Android官方认为手机横屏才是摄像头的正确打开方式,所以保存的图片也是横屏保存的,所以如果想竖屏显示图片,就要把图片顺时针旋转90度,调整到竖屏显示模式,具体代码如下:

    1 private void setImageByte(ImageView showImage, byte[] data) {
    2         Bitmap bitmap= BitmapFactory.decodeByteArray(data, 0, data.length);
    3         showImage.setImageBitmap(bitmap);
    4         Matrix matrix = showImage.getImageMatrix();
    5         matrix.setRotate(90);
    6         showImage.setImageBitmap(Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true));
    7     }

    还有一个问题是解决两个控件内容同时保存的问题,这个其实想到了方法就很简单了,自定义一个FrameLayout的子布局,在该布局中只加入一个方法

    1     public Bitmap getBitmap(){
    2         //设置缓存
    3         setDrawingCacheEnabled(true);
    4         buildDrawingCache();
    5         //从缓存中获取当前屏幕的图片
    6         return getDrawingCache();
    7     }

    然后在上文用到的xml布局中将该布局作为要同时保存图像的父控件。

    最后在java代码中需要保存合成图片的地方,只需要实例化刚刚自定义的FrameLayout对象调用getBitmap(),就能返回合成之后的Bitmap对象;

    所以我们的功能就能完全实现了。

  • 相关阅读:
    Typescript中抽象类与接口详细对比与应用场景介绍
    html5手势操作与多指操作封装与Canvas图片裁切实战
    可编辑DIV与移动端软键盘兼容性问题汇总
    作业系统前端架构总结
    移动端滑屏全应用【四】移动端动画贞动画函数mTween封装
    移动端滑屏全应用【三】requestAnimationFrame的兼容与使用
    python2.7和python3.7的区别!
    通俗易懂地解释卷积
    傅里叶变换就是这么简单!
    大话傅里叶变换,通俗易懂!
  • 原文地址:https://www.cnblogs.com/BobGo/p/5646958.html
Copyright © 2011-2022 走看看