Android中开发相机的两种方式:

Android系统提供了两种使用手机相机资源实现拍摄功能的方法,一种是直接通过Intent调用系统相机组件,这种方法快速方便,适用于直接获得照片的场景,如上传相册,微博、朋友圈发照片等。另一种是使用相机API来定制自定义符合自己需求的相机,这种方法适用于需要定制相机界面或者开发特殊相机功能的场景,如需要对照片做裁剪、滤镜处理,添加贴纸,表情,地点标签等。(我在使用的时候发现Camera已经被摒弃了,API 21中出现了camera2这个类来代替camera类,但是笔者的测试手机还是andorid 4.4,所以还是用Camera)

这里我就简单介绍一下我是怎么实现自定义相机的,虽然界面效果有点low,这里主要介绍一下功能,首先我们需要配置相机权限,由于我这里是将拍照的图片存储在文件夹里面的所以还需要读写文件的权限(权限如下)

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"></uses-permission>
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera.autofocus" />
<uses-feature android:name="android.hardware.camera" />
那么接下来,使用相机我们总需要一个能够看到图像的地方吧,这里Google叫我们使用SurfaceView这个类,那么SurfaceView这个类 是什么呢,首先这个类是继承View的,可以在将图像绘制在屏幕上并显示给用户。其实能够显示的原因是SurfaceView中包含一个Surface对 象,Surface是SurfaceView的可见部分.这里就不详细介绍了.
对于自定义相机我们需要考虑传感器 以及相机的分辨率 预览图片的分辨率 在这里我暂时定义了三个传感器(加速度 ,旋转矢量,陀螺仪)部分代码如下:

public class SensorUtil {//定义3种传感器的数组,依次为加速度>旋转矢量>陀螺仪
   private static int[] mSensorTypes = {Sensor.TYPE_ACCELEROMETER
         , Sensor.TYPE_ROTATION_VECTOR, Sensor.TYPE_GYROSCOPE};

   //是否使用传感器或传感器是否可以使用
   private static boolean mIsStart = false;
   // 定义长度为3的数组保存传感器上一次记录的数据,0,1,2分别对应x,y,z轴数据
   // 初始化记录数据
   private static float[] mLastValues = {0f, 0f, 0f};
   //定义阈值变化的范围
   //定义加速度限定的值
   public final static float LIMIT_ACCELEROMETER = 1.0f;
   //定义旋转矢量限定值
   public final static float LIMIT_ROTATION = 0.8f;
   //定义陀螺仪限定值
   public final static float LIMIT_GYROSCOPE = 0.8f;

   /**
    * @return void 返回类型
    * @throws
    * @Title: restartSensor
    * @param sensorManager 传感器管理器
    * @param listener  A {@link SensorEventListener SensorEventListener} object
    * @author:
    */
   public static  void startSensor(SensorManager sensorManager, SensorEventListener listener) {// 获取当前机器支持的最优的传感器
      Sensor sensor =getBestSensor(sensorManager);
      // 表示未获取到所需的传感器
       if (null == sensor) {Log.d("Sensor", "系统不存在所需的传感器,开启定时器聚焦模式");
       }else{mIsStart=true;
         /**
          * * 注册传感器监听事件
          * * this,表示SensorEventListener
          * * sensor,表示对应的传感器
          * * SensorManager.SENSOR_DELAY_NORMAL,表示传感器的刷新频率
          * */
         sensorManager.registerListener(listener, sensor, SensorManager.SENSOR_DELAY_GAME);
          Log.i("Sensor", "注册传感器");
      }}/**
    * 得到所需的最优的传感器,如果没有下面3个即返回null
    * 权重比较:旋转矢量>陀螺仪>加速度
    * @Title: getBestSensor
    * @Description:
    * @author
    * @date
    * @param sensorMag,系统的传感器管理器
    * @return Sensor
    */
   public static Sensor getBestSensor(SensorManager sensorMag){mIsStart=false;
      //遍历需要用到的3种传感器
      for (int i=0; i<mSensorTypes.length; i++){Sensor sensor = sensorMag.getDefaultSensor(mSensorTypes[i]);
         //获取到存在的传感器返回
         if (sensor!= null){return sensor;
         }}//为找到符合条件的传感器
      return null;
   }/**
    * 返回是否注册了传感器
    * @Title: isSensor
    * @Description:
    * @author
    * @date
    * @return boolean
    */
   public static boolean isStart(){return mIsStart;}public static void setIsStart(boolean isStart){mIsStart=isStart;}/**
    * 比较传感器数据变化是否在可接受的范围内
    * @Title: isOverRange
    * @Description:
    * @author
    * @date
    * @param event,当前传回数据的传感器事件
    * @return
    * @return boolean
    * @throws
    */
   public static boolean isOverRange(SensorEvent event){boolean ok = false;
      //根据不同的传感器进行不同的判断
      switch (event.sensor.getType()){//旋转矢量传感器
      case Sensor.TYPE_ROTATION_VECTOR:ok = compareRotaion(event.values);
         break;
      //陀螺仪传感器
      case Sensor.TYPE_GYROSCOPE:ok = compareGyroscope(event.values);
         break;
      //加速度传感器
      case Sensor.TYPE_ACCELEROMETER:ok = compareAccelerometer(event.values);
         break;
      default:break;
      }// 保存当前的值用于比对
      if (ok) {mLastValues[0] = event.values[0];
         mLastValues[1] = event.values[1];
         mLastValues[2] = event.values[2];
      }return ok;
   }/**
    * 旋转矢量比较
    * @Title: compareRotaion
    * @Description:
    * @author
    * @date
    * @param values,当前的数据
    * @return
    * @return boolean
    * @throws
    */
   private static boolean compareRotaion( float[] values){//比较两次变化的差异值
        float deltaX = Math.abs(values[0] - mLastValues[0]);
        float deltaY = Math.abs(values[1] - mLastValues[1]);
        float deltaZ = Math.abs(values[2] - mLastValues[2]);
        //根据差异值判断是否超过范围
      if (deltaX > SensorUtil.LIMIT_ROTATION
            || deltaY > SensorUtil.LIMIT_ROTATION
            || deltaZ > SensorUtil.LIMIT_ROTATION){Log.i("haha", ">>>>>overRange");
         return true;
      }return false;
   }/**
    * 陀螺仪比较
    * @Title: compareGyroscope
    * @Description:
    * @author
    * @date
    * @param values,当前数据
    * @return
    * @return boolean
    * @throws
    */
   private static boolean compareGyroscope( float[] values){//比较两次变化的差异值
        float delateX = Math.abs(values[0] - mLastValues[0]);
        float delateY = Math.abs(values[1] - mLastValues[1]);
        float delateZ = Math.abs(values[2] - mLastValues[2]);
        //根据差异值判断是否在阈值范围类
      if (delateX > SensorUtil.LIMIT_GYROSCOPE
            || delateY > SensorUtil.LIMIT_GYROSCOPE
            || delateZ > SensorUtil.LIMIT_GYROSCOPE){return true;
      }return false;
   }/**
    * 加速度比较
    * @Title: compareGyroscope
    * @Description:
    * @author
    * @date
    * @param values,当前数据
    * @return
    * @return boolean
    * @throws
    */
   private static boolean compareAccelerometer(float[] values){//比较两次变化的差异值
        float delateX = Math.abs(values[0] - mLastValues[0]);
        float delateY = Math.abs(values[1] - mLastValues[1]);
        float delateZ = Math.abs(values[2] - mLastValues[2]);
        //通过差异值判断是否在阈值内
      if (delateX > SensorUtil.LIMIT_ACCELEROMETER
            || delateY > SensorUtil.LIMIT_ACCELEROMETER
            || delateZ > SensorUtil.LIMIT_ACCELEROMETER){return true;
      }return false;
   }}

 自定义相机拍照快门声音(部分代码如下):

public class SoundUtils {
    //定义左右声道的音量大小
    public final static float LEFT_VOLUME = 1.0f;
    public final static float RIGHT_VOLUME = 1.0f;
    /**
     *
     * @Title: playerScanOkWav
     * @Description: R.raw.scan_ok
     * @author
     * @date 2
     * @param @param context    设定文件
     * @param type 0:扫描;1:拍照
     * @return void    返回类型
     * @throws
     */
    public final static void playerScanOkWav(Context context, int type){int sound =R.raw.scan_ok;
        if(type == 1){sound = R.raw.camera;
        }MediaPlayer mediaPlayer = MediaPlayer.create(context,sound);
        mediaPlayer.setVolume(LEFT_VOLUME, RIGHT_VOLUME);
        mediaPlayer.start();
    }
}

 点击拍照按钮时进入预览图像界面需要 打开相机调用Camera.open()的方法:

/**
*@date 创建时间 2017/4/15
 *@author
 *@company
 *@name:zhongshuiping
 *@Description  打开相机
 */
public Camera getCameraInstance() {Camera c = null;
    try {c = Camera.open();
        // 打开相机异常
    } catch (Exception e) {}return c;
}

当我们拍照完成结束界面时需要启动关闭相机的功能:

/**
 *@date 创建时间 2017/4/15
 *@author
 *@company
 *@name:zhongshuiping
 *@Description关闭相机
 */
@Override
public void surfaceDestroyed(SurfaceHolder holder) {if (flashlight) {if (mCamera != null) {Parameters params = mCamera.getParameters();
            // 关闭闪光灯
            params.setFlashMode(Parameters.FLASH_MODE_OFF);
        }flashlightBtn.setBackgroundResource(R.drawable.flashlightclose);
        flashlight = false;
    }if (mCamera != null) {mCamera.setPreviewCallback(null);
        mCamera.stopPreview();
        mCamera.release();
        mCamera = null;
    }// 判断是否开启传感器监听并注销监听
    SensorCancellation();
}

传感器改变事件:

/**
 *@date 创建时间 2017/4/15
 *@author
 *@company
 *@name:zhongshuiping
 *@Description 传感器精度改变事件
 */
@Override
public void onAccuracyChanged(Sensor arg0, int arg1) {}
 /**
     *@date 创建时间 2017/4/15
     *@author
     *@company
     *@name:zhongshuiping
     *@Description 传感器改变事件
     */
    @Override
    public void onSensorChanged(SensorEvent event) {// 判断相机是否准备好并且手机移动超过一定的范围
        if (mCamera != null && SensorUtil.isStart() && SensorUtil.isOverRange(event)&&!bIsFocusing) {// 调用自动聚焦回调
            bIsFocus = false;
            bIsFocusing = true;
            finder_view.bFocused = false;
//            watermark.setVisibility(View.VISIBLE);
            finder_view.invalidate();
            //Log.i(TAG, "==================================onSensorChanged bIsFocus = false");

            mCamera.cancelAutoFocus();// 只有加上了这一句,才会自动对焦
            mCamera.autoFocus(autoFocusCB);
        }}
/**
 *@date 创建时间 2017/4/15
 *@author
 *@company
 *@name:zhongshuiping
 *@Description  传感器注销事件
 */
private void SensorCancellation(){if (SensorUtil.isStart()) {SensorUtil.setIsStart(false);
        sensorMag.unregisterListener(PhotographActivity.this);
    }
}

匹配图片分辨率这里我循环查找该手机所以快高分辨率比由高往低查找直到找到16/9的比例的分辨率由于我这里的对焦框是固定的图片对焦框比例是按照16/9的比例绘制的:

/**
 * 设定的屏幕的比例不是图片的比例
 *@date 创建时间 2017/4/15
 *@author
 *@company
 *@name:zhongshuiping
 *@Description 匹配分辨率
 */
private Size getOptimalPreviewSize(List<Size> sizes, double targetRatio) {if (sizes == null)return null;
    Size optimalSize = null;
    Collections.sort(sizes, new Comparator<Size>() {@Override
        public int compare(Size lhs, Size rhs) {return new Double(lhs.width).compareTo(new Double(rhs.width));
        }});
    for (int i=sizes.size()-1;i>=0;i--) {Size size = sizes.get(i);
        if ((( Constants.EIGHT_HUNDRED < size.width && size.width < Constants.TWO_THOUSAND)|| (Constants.EIGHT_HUNDRED< size.height && size.height < Constants.TWO_THOUSAND))&& ((size.width * 9) == (size.height * 16) )) {optimalSize = size;
            break;
        }}return optimalSize;
}
/**
 * 设置的是拍照的图片的比例
 *@date 创建时间 2017/4/15
 *@author
 *@company
 *@name:zhongshuiping
 *@Description 匹配分辨率
 */
private Size getOptimalPictureSize(List<Size> sizes, double targetRatio) {if (sizes == null)return null;
    Size optimalSize = null;
    Collections.sort(sizes, new Comparator<Size>() {@Override
        public int compare(Size lhs, Size rhs) {return new Double(lhs.width).compareTo(new Double(rhs.width));
        }});
    for (int i=sizes.size()-1;i>=0;i--) {Size size = sizes.get(i);
        if (((Constants.NUMBER_ONE_THOUSAN < size.width && size.width < Constants.NUMBER_TWO_THOUSAND)|| (Constants.NUMBER_ONE_THOUSAN < size.height && size.height < Constants.NUMBER_TWO_THOUSAND))&& ((size.width * 9) ==(size.height * 16) )) {optimalSize = size;
            break;
        }}/**如果没找到16/9的就选择最接近的*/
    if(optimalSize == null){double dMin = 100.0;
        Size RightSize = null;
        for (Size size : sizes) {double fRate = size.width/(float)size.height;
            double fDistance = Math.abs(fRate - 16.0/9.0);
            //找最接近16比9的size;
            if(fDistance < dMin){dMin = fDistance;
                RightSize = size;
            }}//最接近的值赋给变量optimalSize
        optimalSize = RightSize;
    }return optimalSize;
}

对图片进行处理我们需要时需要释放资源:

 

/**
 *@date 创建时间 2017/4/15
 *@author
 *@company
     *@name:zhongshuiping
 *@Description Activity被暂停或收回cpu和其他资源时调用时调stopPreview释放资源
 */
public void onPause() {super.onPause();
    sensorMag.unregisterListener(this);
    stopPreview();
}
/**
 *@date 创建时间 2017/4/15
 *@author
 *@company
 *@name:zhongshuiping
 *@Description 释放资源
 */
private void stopPreview() {if (mCamera != null) {try {mCamera.setPreviewDisplay(null);
            mCamera.stopPreview();
        } catch (Exception e) {}}
}

在拍照场景不是很理想的情况小我们需要打开闪光灯,辅助我们达到一个比较理想的拍照场景

 

/**
 *@date 创建时间 2017/4/15
 *@author
 *@company
 *@name:zhongshuiping
 *@Description 闪光灯操作
 */
private void openFlashLamp(){if (mCamera == null) {mCamera = getCameraInstance();
    }Parameters params = mCamera.getParameters();
    /**
     * 闪光灯
     */
    if (flashlight) {// 关闭闪光灯
        params.setFlashMode(Parameters.FLASH_MODE_OFF);
        flashlightBtn.setBackgroundResource(R.drawable.zx_code_closelight);
        flashlight = false;
    } else {// 打开闪光灯
        params.setFlashMode(Parameters.FLASH_MODE_TORCH);
        flashlightBtn.setBackgroundResource(R.drawable.zx_code_openlight);
        flashlight = true;
    }mCamera.setParameters(params);
}

相机的聚焦是通过传感器来时时聚焦的 这里我设置了可手动聚焦 通过点击屏幕聚焦,这里的聚焦是全屏聚焦跟原生相机不一样,原生相机是触碰点附近聚焦:

 

   /**
     *@date 创建时间 2017/4/15
     *@author
     *@company
     *@name:zhongshuiping
     *@Description AutoFocusCallback自动对焦
     */
    AutoFocusCallback autoFocusCB = new AutoFocusCallback() {public void onAutoFocus(boolean success, Camera camera) {bIsFocusing = false;
            if (success) {bIsFocus = true;
                finder_view.bFocused = true;
//                watermark.setVisibility(View.GONE);
                finder_view.invalidate();
            } else {bIsFocus = false;
                finder_view.bFocused = false;
//                watermark.setVisibility(View.VISIBLE);
                finder_view.invalidate();
            }}};

   

  以上是自定义相机的部分代码详细代码如下地址:

  http://download.csdn.net/download/androidzsp/10024882


android自定义相机拍照相关推荐

  1. Android自定义相机拍照、图片裁剪的实现

    原文:Android自定义相机拍照.图片裁剪的实现 最近项目里面又要加一个拍照搜题的功能,也就是用户对着不会做的题目拍一张照片,将照片的文字使用ocr识别出来,再调用题库搜索接口搜索出来展示给用户,类 ...

  2. android 自定义拍照模糊,Android自定义相机拍照模糊处理

    问题分析:随着用户对于拍照清晰度的需求,android手机对于摄像头也是一升再升,这就导致了作为android开发工程师对于兼容性维护的继续跟进以及问题处理. 针对于自定义相机拍照模糊的问题,经过几天 ...

  3. android自定义相机拍照反面,这部六年前的安卓手机,背面堪比数码相机“能伸能缩”...

    原标题:这部六年前的安卓手机,背面堪比数码相机"能伸能缩" 最近端午小长假在朋友家玩,发现了一款很神奇的手机.起初看它的正面,我以为是三星Galaxy S4,但拿起来后发现手感明显 ...

  4. android 实现自动拍照,Android自定义相机实现定时拍照功能

    这篇博客为大家介绍Android自定义相机,并且实现倒计时拍照功能. 首先自定义拍照会用到SurfaceView控件显示照片的预览区域,以下是布局文件: activity_main.xml andro ...

  5. Android自定义相机实现定时拍照

    这篇博客为大家介绍Android自定义相机,并且实现倒计时拍照功能. 首先自定义拍照会用到SurfaceView控件显示照片的预览区域,以下是布局文件: activity_main.xml <F ...

  6. uni-app 自定义相机拍照录像,可设置分辨率、支持横竖屏(ios、android)

    插件市场:uni-app 自定义相机拍照录像,可设置分辨率.支持横竖屏(ios.android)

  7. Android自定义相机,切换前后摄像头,照相机拍照

    Android自定义相机,简单实现切换前后摄像头,照相机拍照 Ctrl +C  Ctrl+V 可以直接 run 起来,注释比较详细;源码下载 <?xml version="1.0&qu ...

  8. android 自定义相机,Android自定义相机实现定时拍照功能

    这篇博客为大家介绍Android自定义相机,并且实现倒计时拍照功能. 首先自定义拍照会用到SurfaceView控件显示照片的预览区域,以下是布局文件: activity_main.xml andro ...

  9. android 自定义相机源码,Android 自定义相机及分析源码

    Android 自定义相机及分析源码 使用Android 系统相机的方法: 要想让应用有相机的action,咱们就必须在清单文件中做一些声明,好让系统知道,如下 action的作用就是声明action ...

最新文章

  1. pandas使用query函数删除dataframe中某一列数值等于某一特定值的行(remove dataframe rows baed on column value)
  2. 复杂性理论研究的核心问题是什么
  3. 自定义函数变量的设置(*/**),lambda匿名函数(map/filter/zip/enumerate)
  4. [数分提高]2014-2015-2第8教学周第1次课 (2015-04-21)
  5. android studio 工程demo例子,Android Studio 实现跑马灯微项目实例
  6. 为什么我不信任通配符,以及为什么我们仍然需要通配符
  7. 数据分析方法-聚类算法
  8. JS:鼠标事件:实现鼠标移动到div背景颜色变换,移开还原
  9. linux window manager,Window manager (简体中文)
  10. 如何快速不借用转换工具将FLV格式视频转换成MP4
  11. 如何 拆 贴片电容 而不是把焊盘给搞坏
  12. adaboost.M1与adaboost.M2差别比较
  13. WIN10和Ubuntu共享蓝牙连接配对
  14. addEventListener()事件监听
  15. BUUCTF:[安洵杯 2019]吹着贝斯扫二维码
  16. 基于网络中心性的计算机网络脆弱性评估方法
  17. Mapxtreme之活活气死
  18. 高铁乘务员搜题软件哪个好?工资一般多少?
  19. 百姓网上怎么引流?百姓网如何发帖引流?百姓网如何推广?
  20. 程序员踩点下班,领导:不想干的请办理离职,我这里不养闲人与废物

热门文章

  1. CentOS6.5搭建asterisk及配置软电话
  2. 东南大学2012年招收推荐免试生直接攻读博士学位研究生简章
  3. DR和DIS的区别?
  4. 加密项目是否采用DAO模式 首先考量这8个因素
  5. 项目难做,程序员难当,软件开发中的 9 大难题
  6. EXCEL表格内部换行
  7. 要怎么在计算机里清除桌面内存,怎么清理电脑内存 最全电脑内存清理方法
  8. python方差齐性检验_方差分析中的方差齐性检验_方差齐性检验结果分析
  9. 25分钟破亿 首销火爆的moto edge X30给友商们出了个难题
  10. markdown中数学符号公式和字母表示