基础

相机开发中常用的API
  • package android.hardware.camera2 这个包中包含了主要的相机控制的API,可以用于在你的项目中进行拍照或者录像功能的实现
  • Camera 这个类是早些版本驱动相机的API
  • SurfaceView 用于相机预览的界面
  • MediaRecorder 用于录像的API
  • 通过Intent调用系统的拍照功能,通过设置MediaStore.ACTION_IMAGE_CAPTURE 或者 MediaStore.ACTION_VIDEO_CAPTURE 动作调用系统的拍照功能
权限声明
  • 相机开发权限声明
<uses-permission android:name="android.permission.CAMERA" />
  • 其他功能,如GPS定位权限声明
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
调用本机相机功能
  • 开启相机
private static final int CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE = 100;
private Uri fileUri;@Override
public void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.main);// 创建Intent对象并传递相应的action参数Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);fileUri = getOutputMediaFileUri(MEDIA_TYPE_IMAGE); // 创建一个文件夹保存图片intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri); // 设置保存路径// 开启相机startActivityForResult(intent, CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE);
}
  • 接收返回结果
private static final int CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE = 100;
private static final int CAPTURE_VIDEO_ACTIVITY_REQUEST_CODE = 200;@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {if (requestCode == CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE) {if (resultCode == RESULT_OK) {// Image captured and saved to fileUri specified in the IntentToast.makeText(this, "Image saved to:\n" +data.getData(), Toast.LENGTH_LONG).show();} else if (resultCode == RESULT_CANCELED) {// User cancelled the image capture} else {// Image capture failed, advise user}}if (requestCode == CAPTURE_VIDEO_ACTIVITY_REQUEST_CODE) {if (resultCode == RESULT_OK) {// Video captured and saved to fileUri specified in the IntentToast.makeText(this, "Video saved to:\n" +data.getData(), Toast.LENGTH_LONG).show();} else if (resultCode == RESULT_CANCELED) {// User cancelled the video capture} else {// Video capture failed, advise user}}
}

构建一个相机APP

相机开发的基本步骤
  1. 检查是否能够使用相机
  2. 创建一个相机的预览类,该类继承自SurfaceView并且实现SurfaceHolder接口,用于接收并显示camera传递过来的数据
  3. 构建一个预览布局,该布局包含相机预览类以及用户自定义的控制接口
  4. 设置监听事件,捕获用户拍照或者录像动作,并作出相应的响应
  5. 将捕获到的照片或者录像保存
  6. 释放Camera资源,通过camera.release();方法
分步骤
1,检测相机功能是否可以使用

通过PackageManager中的hasSystemFeature()方法判断,示例代码如下:

/** Check if this device has a camera */
private boolean checkCameraHardware(Context context) {if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)){// this device has a camerareturn true;} else {// no camera on this devicereturn false;}
}

此外,2.3版本以后,可以通过Camera.getNumberOfCameras()方法来获取设备中相机的数量,可以用来判断是否有前置摄像头

2,获取相机实例

使用Camera类的静态方法open()来获取相机实例

/** A safe way to get an instance of the Camera object. */
public static Camera getCameraInstance(){Camera c = null;try {c = Camera.open(); // attempt to get a Camera instance
    }catch (Exception e){// Camera is not available (in use or does not exist)} return c; // returns null if camera is unavailable
}

注意:当camera正在被使用或者不存在的时候,应用将会直接退出。此外,在2.3版本或者更高版本中,还可以使用open(int)方法获取Camera的实例,传入得参数int类似于优先级的作用

3,相机参数的获取

通过camera.getParameters()方法获取到相机相对应的参数,并通过Parameter对象进行设置修改,在2.3及之后的版本,通过camera.getCameraInfo()方法来确定摄像头是否为前置或者后置,以及Image的方向

4,创建相机预览类
/** A basic Camera preview class */
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {private SurfaceHolder mHolder;private Camera mCamera;public CameraPreview(Context context, Camera camera) {super(context);mCamera = camera;// Install a SurfaceHolder.Callback so we get notified when the// underlying surface is created and destroyed.mHolder = getHolder();mHolder.addCallback(this);// 不赞成设置,要求在3.0版本之前使用mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);}public void surfaceCreated(SurfaceHolder holder) {// 通知Camera预览图片放哪里,即将SurfaceView与相机绑定try {mCamera.setPreviewDisplay(holder);mCamera.startPreview();} catch (IOException e) {Log.d(TAG, "Error setting camera preview: " + e.getMessage());}}public void surfaceDestroyed(SurfaceHolder holder) {// empty.注意要在你的活动中释放相机预览}public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {// If your preview can change or rotate, take care of those events here.// 确保在重新设置大小或者格式化之前停止预览if (mHolder.getSurface() == null){// preview surface does not existreturn;}// stop preview before making changes//停止预览try {mCamera.stopPreview();} catch (Exception e){// ignore: tried to stop a non-existent preview}// set preview size and make any resize, rotate or// reformatting changes here//重新设置代码编写//设置预览大小尺寸应该从方法getSupportPreviewSizes()方法中获取// start preview with new settings//开始预览try {mCamera.setPreviewDisplay(mHolder);mCamera.startPreview();} catch (Exception e){Log.d(TAG, "Error starting camera preview: " + e.getMessage());}}
}
5,创建简单的相机预览界面布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="horizontal"android:layout_width="match_parent"android:layout_height="match_parent"><FrameLayoutandroid:id="@+id/camera_preview"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_weight="1"/><Buttonandroid:id="@+id/button_capture"android:text="Capture"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"/>
</LinearLayout>

当中的FramLayout相当于是相机预览的容器,而Button则是实现点击拍照,通常情况,相机画面默认的方向是水平方向的,所以需要将Activity的方向调整成水平方向,

activity android:name=".CameraActivity"android:label="@string/app_name"android:screenOrientation="landscape"><!-- configure this activity to use landscape orientation --><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter>
</activity>

或者通过Parameter的setRotation()方法对方向进行调整

//根据当前朝向修改保存图片的旋转角度private void updateCameraOrientation(){if(mCamera!=null){Camera.Parameters parameters = mCamera.getParameters();//rotation参数为 0、90、180、270。水平方向为0。int rotation=90+mOrientation==360?0:90+mOrientation;//前置摄像头需要对垂直方向做变换,否则照片是颠倒的if(mIsFrontCamera){if(rotation==90) rotation=270;else if (rotation==270) rotation=90;}parameters.setRotation(rotation);//生成的图片转90°//预览图片旋转90°mCamera.setDisplayOrientation(90);//预览转90°mCamera.setParameters(parameters);}}

构建出相机的布局后,将相机预览类放到布局的FramLayout中,具体代码如下:

public class CameraActivity extends Activity {private Camera mCamera;private CameraPreview mPreview;@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.main);// Create an instance of CameramCamera = getCameraInstance();// Create our Preview view and set it as the content of our activity.mPreview = new CameraPreview(this, mCamera);FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview);preview.addView(mPreview);}
}
6-1 实现拍照功能
  • 拍照动作的实现,主要是通过Camera.takePicture()方法完成,该方法接收三个参数,分别为三个回调事件,ShutterCallback以及两个PictureCallback,为了接收图片,第三个PictureCallback必须实现,用来保存camera当中拍到的图片,简单的实现如下:
private PictureCallback mPicture = new PictureCallback() {@Overridepublic void onPictureTaken(byte[] data, Camera camera) {File pictureFile = getOutputMediaFile(MEDIA_TYPE_IMAGE);if (pictureFile == null){//对象为空,则文件不存在,直接返回Log.d(TAG, "Error creating media file, check storage permissions: " +e.getMessage());return;}try {FileOutputStream fos = new FileOutputStream(pictureFile);fos.write(data);fos.close();} catch (FileNotFoundException e) {Log.d(TAG, "File not found: " + e.getMessage());} catch (IOException e) {Log.d(TAG, "Error accessing file: " + e.getMessage());}}
};

通过给布局中的Button设置监听事件,当按钮被点击时触发拍照功能,即调用Camera.takePicture()方法

// Add a listener to the Capture button
Button captureButton = (Button) findViewById(id.button_capture);
captureButton.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {// get an image from the cameramCamera.takePicture(null, null, mPicture);}}
);

通过以上代码即可简单实现拍照功能

6-2 实现录像功能
  • 相比较于拍照,录像功能的实现需要更加细致的管理,需要Camera同另一个类MediaRecorder协同配合完成。在使用MediaRecorder之前,还需要调用Camera.lock()和Camera.unlock()方法来允许MediaRecorder对象调用camera硬件。从4.0版本开始,这两个方法将会自动被调用,而不需要手动调用它们。
  • 与拍照不同,录像功能的实现必须按照指定的顺序执行才能成功,详细步骤如下
    1. 打开相机,获取到Camera的实例
    2. 绑定对应的预览SurfaceView,通过Camera.setPreviewDisplay()方法
    3. 调用Camera.startPreview()方法开启预览
    4. 开启录像功能,一下步骤必须全部实现
      1. 解锁相机,通过Camera.unlock()方法
      2. 配置MediaRecorder
        1. setCamera(),设置相机为录像功能,

释放Camera资源

Camera在内润是单例的,所有的程序都可以访问和获取该实例,而一旦程序在使用完Camera对象后没有及时的释放该实例,将会导致该资源不能被其他的程序所访问,所有访问Camera类都会被系统关闭。释放Camera的方法为Camera.release();实例代码如下:

public class CameraActivity extends Activity {private Camera mCamera;private SurfaceView mPreview;private MediaRecorder mMediaRecorder;...//在Activity的onPause方法中释放资源,如果同时调用了MediaRecorder,也需要将MediaRecorder一同释放@Overrideprotected void onPause() {super.onPause();// if you are using MediaRecorder, release it firstreleaseMediaRecorder();       // release the camera immediately on pause eventreleaseCamera();             }private void releaseMediaRecorder(){if (mMediaRecorder != null) {// clear recorder configuration//释放前先清除MediaRecorder中的配置mMediaRecorder.reset();  // release the recorder object//释放MediaRecorder实例mMediaRecorder.release(); mMediaRecorder = null;// lock camera for later use//将Camera锁上,后面释放和调用时使用mCamera.lock();           }}private void releaseCamera(){if (mCamera != null){// release the camera for other applicationsmCamera.release();        mCamera = null;}}
}

文件存储

  • 使用后生成的Image或者Video都应该保存在sd卡中,保存的路径有以下两个:

    • Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),该方法中的路径是公有的,共享的,所有的应用都可以通过该路径得到路径下的图片资源文件,同时对其进行修改、删除等操作,也是推荐的保存路径。此外,当应用程序被卸载时,保存在该路径下的图片文件并不会删除,而会一直保存下来。
    • context.getExternalFileDir(Enviroment.DIRECTORY_PICTURES);该路径下保存的Image或者Video资源将会和你的Application关联,其他的程序不能对其中的资源进行访问、修改以及删除等操作;但当应用程序被卸载时,这些资源也将会被删除。
//示例代码
public static final int MEDIA_TYPE_IMAGE = 1;
public static final int MEDIA_TYPE_VIDEO = 2;/** Create a file Uri for saving an image or video */
//获取Uri地址
private static Uri getOutputMediaFileUri(int type){return Uri.fromFile(getOutputMediaFile(type));
}/** Create a File for saving an image or video */
private static File getOutputMediaFile(int type){// To be safe, you should check that the SDCard is mounted// using Environment.getExternalStorageState() before doing this.//在使用之前最好先检查sd卡是否挂载File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "MyCameraApp");// This location works best if you want the created images to be shared// between applications and persist after your app has been uninstalled.// Create the storage directory if it does not existif (! mediaStorageDir.exists()){//如果创建失败,返回nullif (! mediaStorageDir.mkdirs()){Log.d("MyCameraApp", "failed to create directory");return null;}}// Create a media file name//根据当前时间创建文件名String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());File mediaFile;if (type == MEDIA_TYPE_IMAGE){mediaFile = new File(mediaStorageDir.getPath() + File.separator +"IMG_"+ timeStamp + ".jpg");} else if(type == MEDIA_TYPE_VIDEO) {mediaFile = new File(mediaStorageDir.getPath() + File.separator +"VID_"+ timeStamp + ".mp4");} else {return null;}return mediaFile;
}

注意:Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)方法是在2.2(APILevel 8)版本之后的,更早的版本应该使用Environment.getExternalStorageDirectory()替换。

Camera属性

  • camera中大部分的属性都可以通过Camera.Paramter中进行设置,详细属性说明看官方文档,以下三个属性还需要进行其他的设置

    • Metering and focus area
    • Face detection
    • Time lapse video
  • 不是所有的camera属性都是可以使用的,要根据手机硬件是否支持以及相对应的软件是否实现该功能。

检测Camera中的属性是否可用

camera中的一些属性的使用是取决于手机的硬件以及软件是否支持,所以在考虑使用这些属性的时候,就需要对手机是否支持进行检测判断,例如:

// get Camera parameters
Camera.Parameters params = mCamera.getParameters();List<String> focusModes = params.getSupportedFocusModes();
if (focusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) {// Autofocus mode is supported
}

Camera.Paramters中的大部分属性是否支持都可以通过getSupport...()或者is...Supported()或者getMax()方法获取,需要注意的是,当你检测和设置了这些属性之后,Google Play将会约束你的应用不能在不支持这些功能的手机上进行安装。

  • 属性设置
// get Camera parameters
Camera.Parameters params = mCamera.getParameters();
// set the focus mode
params.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
// set Camera parameters
mCamera.setParameters(params);

相机中大部分属性在修改过后就能立刻在Preview当中显示出来,不过图片的尺寸以及方向是不能立刻显示的,需要先停止预览,设置大小及方向,最后再重新开始预览才能显示出来,从4.0版本(APILevel 14)开始,预览的可以不需要重新启动就被改变。

设置感光和聚焦区域

在4.0版本(APILevel 14)之后,可以指定区域对聚焦以及感光进行指定的设置,以下为设置两个感光区域的示例代码:

// Create an instance of Camera
mCamera = getCameraInstance();// set Camera parameters
Camera.Parameters params = mCamera.getParameters();if (params.getMaxNumMeteringAreas() > 0){ // check that metering areas are supportedList<Camera.Area> meteringAreas = new ArrayList<Camera.Area>();Rect areaRect1 = new Rect(-100, -100, 100, 100);    // specify an area in center of image// set weight to 60%//设置权重meteringAreas.add(new Camera.Area(areaRect1, 600)); Rect areaRect2 = new Rect(800, -1000, 1000, -800);  // specify an area in upper right of imagemeteringAreas.add(new Camera.Area(areaRect2, 400)); // set weight to 40%params.setMeteringAreas(meteringAreas);
}mCamera.setParameters(params);

注意:通过Camera.setDisplayOrientation()改变屏幕方向,原坐标系统不会重绘。

人脸识别

  • 从4.0版本(APILevel 14)之后,提供了可以进行人脸识别的API,需要注意的是,在进行人脸识别的时候,setWhiteBalance(String), setFocusAreas(List)和setMeteringAreas(List) 将不会有效。
  • 人脸识别功能的开发步骤:
    • 检查设备当中是否支持该功能,通过getMaxNumDetectedFaces()方法进行获取
    • 创建相应的人脸识别监听并绑定到相机上
    • 在预览后开启人脸识别
//人脸识别监听器
class MyFaceDetectionListener implements Camera.FaceDetectionListener {@Overridepublic void onFaceDetection(Face[] faces, Camera camera) {if (faces.length > 0){Log.d("FaceDetection", "face detected: "+ faces.length +" Face 1 Location X: " + faces[0].rect.centerX() +"Y: " + faces[0].rect.centerY() );}}
}
  • 绑定监听器
//绑定监听器
mCamera.setFaceDetectionListener(new MyFaceDetectionListener());
  • 开启人脸识别功能方法
//开启人脸识别功能方法
public void startFaceDetection(){// Try starting Face DetectionCamera.Parameters params = mCamera.getParameters();// start face detection only *after* preview has started//确保开启该功能之前开启了预览if (params.getMaxNumDetectedFaces() > 0){// camera supports face detection, so can start it:mCamera.startFaceDetection();}
}
  • 开启的完整代码
public void surfaceCreated(SurfaceHolder holder) {try {mCamera.setPreviewDisplay(holder);mCamera.startPreview();//开启相机预览后再开启人脸识别startFaceDetection(); // start face detection feature} catch (IOException e) {Log.d(TAG, "Error setting camera preview: " + e.getMessage());}
}public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {if (mHolder.getSurface() == null){// preview surface does not existLog.d(TAG, "mHolder.getSurface() == null");return;}try {mCamera.stopPreview();} catch (Exception e){// ignore: tried to stop a non-existent previewLog.d(TAG, "Error stopping camera preview: " + e.getMessage());}try {mCamera.setPreviewDisplay(mHolder);mCamera.startPreview();startFaceDetection(); // re-start face detection feature} catch (Exception e){// ignore: tried to stop a non-existent previewLog.d(TAG, "Error starting camera preview: " + e.getMessage());}
}

Android的相机开发相关推荐

  1. android之相机开发

     http://blog.csdn.net/jason0539/article/details/10125017 android之相机开发 分类: android 基础知识2013-08-20 22: ...

  2. 【转】Android Camera 相机开发详解

    在Android 5.0(SDK 21)中,Google使用Camera2替代了Camera接口.Camera2在接口和架构上做了巨大的变动, 但是基于众所周知的原因,我们还必须基于 Android ...

  3. Android camera相机开发拍照功能

    在Android 5.0(SDK 21)中,Google使用Camera2替代了Camera接口.Camera2在接口和架构上做了巨大的变动,但是基于众所周知的原因,我们还必须基于 Android 4 ...

  4. android 美颜相机开发,Android OpenGL ES从入门到进阶(一)—— 五分钟开发一款美颜相机...

    源码链接:https://github.com/smzhldr/AGLFramework 一.前言 商店里有数十款的美颜相机类产品,以及像抖音,唱吧之类带有视频的软件,功能很强大,其实现原理基本上都是 ...

  5. Android: Camera2相机开发 知识储备

    进行Camera开发主要用到了以下两个类: 1.Camera 2.TexttureView(也可以是SurfaceView) 一.Surface Surfaces是用来处理屏幕显示内容合成器所管理的原 ...

  6. android水印相机,Android 水印相机开发

    水印相机是自定义相机的一种,实现方法有很多,我看了很多别人的做的很漂亮,我做的就很普通了,不过总算是实现了拍照加水印的功能. 我这边用到了SurfaceView,有人没用这个也做出来水印相机,个人觉得 ...

  7. Android Camera相机开发示例、Android 开发板 USB摄像头采集、定期拍照、定时拍照,安卓调用摄像头拍照、Android摄像头预览、监控,USB摄像头开发、摄像头监控代码

    我们有个需求,在机器上加个摄像头,定时拍照: 我到网上搜索,发现没有快速上手和简单使用的: 个人感觉,大部分博客写得很乱,或者长篇大论: 而我只想简单实现功能,并不打算学习多少理论: 下面代码是我写来 ...

  8. 连载 | Android之Camera1实现相机开发

    一.前言 现在很多app都会有拍照功能,一般调用系统进行拍照裁剪就能满足平时的需求,但有些场景或者特殊情况下如:持续不间断拍多张照片或者是进行人脸识别的时候,这时候之间调用系统原生相机拍照时不能满足自 ...

  9. Android之Camera1实现相机开发

    一.前言 现在很多app都会有拍照功能,一般调用系统进行拍照裁剪就能满足平时的需求,但有些场景或者特殊情况下如:持续不间断拍多张照片或者是进行人脸识别的时候,这时候之间调用系统原生相机拍照时不能满足自 ...

最新文章

  1. php中this,self,parent三个关键字
  2. java 按钮不显示文字_java – 使JButton中的文本不可见
  3. 关于tableview的优化
  4. HTML5之article元素与section元素之间的区别?
  5. 排球制作html,MAYA打造排球建模教程
  6. ADO.NET的最佳实践技巧
  7. 国内几大最让游戏建模师羡慕的企业,你想进去吗?
  8. 什么是引发?Java运行时系统引发的异常如何处理?
  9. Mybatis学习随笔
  10. 01背包 Codeforces Round #267 (Div. 2) C. George and Job
  11. mysql查询季度数据统计_mysql按年度、季度、月度、周、日SQL统计查询代码
  12. 最大似然估计程序c语言,极大似然估计(示例代码)
  13. 2022年最新四川建筑八大员(劳务员)模拟题库及答案
  14. 打印 条码 CodeSoft JsBarCode
  15. linux终端网易云播放问题,Ubuntu下完美解决网易云音乐无法启动的问题
  16. 路由器连接光猫用桥接模式好还是用路由模式好?路由器桥接模式vs路由模式
  17. jquery 手型 鼠标穿过时_css各种手型集合(css禁止手型)-Fun言
  18. 使用docker快速体验OceanBase(Win10 Home)
  19. 幻14 2021 R9 5900HS RTX3060 AX210装ubuntu18.04.5笔记
  20. Flexl连接外部的URL

热门文章

  1. 《金文女神解说VB基础入门系列视频教程》300集 第一章笔记
  2. Amazon S3 服务15岁生日快乐!
  3. 个人网站如何取消(注销)备案信息?
  4. php鲜花销售系统论文,鲜花销售管理系统毕业论文.doc
  5. 从苏宁电器到卡巴斯基(后传)第05篇:聊聊我对WannaCry产生的感慨
  6. qq号的正则表达式html,JavaScript中的正则表达式使用及验证qq号码的正则
  7. win7 odbc access 2003 win7下ODBC数据源ACCESS2003的链接
  8. tflearn入门笔记
  9. 解决了一个Web网页显示不全的BUG
  10. 群晖NAS用迅雷远程下载Xware 1.xxx的进程监视和掉线自动重启