开始之前
这是我第一次写博客,现在想想我已经学习Android快两年,但仍记得那个说十年太久,希望两年后的你还记得自己最初的模样的少年。虽然现在我没有能够成为两年前希望的模样,但也有了一份不错的工作,算是没有对不起那个执着的少年。从今天起我决定开始写博客,来记录自己的成长。
今天我想要写的是我们Android App开发中比较常用的摄像头相关的知识,之所以写这个,是因为最近在公司的项目中用到了拍照的功能。更重要的是相机现在作为每一款手机所必备的功能,比如拍照、录制短视频、扫描二维码以及人脸识别等都需要用到Camera。因此作为一个好的Android App开发者怎么能对Camera不了解呢。起初我认为在开发中对Camera的使用仅限于调用系统相机进行拍照,然后拍照后获取拍照的图片,觉得这个没有什么还需要学习。后来在项目的开发中发现我错了呀,Camera在实际开发中不仅仅是调用系统相机来拍照,还需要我们去自定义相机来进行拍照和录像等功能。
Camera两种常见的使用方式
1、调用系统相机、或者具有相机功能的应用
2、自定义相机
一、调用系统相机的步骤
方法一:不指定图片保存的路径,通过Activity的onActivityResult()中获取拍照的图片(Bitmap)
1、在清单文件中添加相机权限
2、使用隐式Intent的跳转到系统相机
3、在重写的onActivityResult的回调方法中获取拍照所得的图片
4、运行Demo的效果图
总结:这种方法实现还是比较简单的,但我们通过运行Demo的效果gif图可以看出在onActivityResult()中获取到的图片是很小的一张经过压缩的图片。我通过debug可以查看获取到的图片bitmap的尺寸大小是194*262。这个图片压缩还是很严重的,只适用于对图片要求不高的场景。
方法二:指定图片保存的地址,还是在onActivityResult方法中获取拍照的图片
1、添加相机以及读取sd卡的权限
2、跳转到系统相机拍照,并指定拍照原图的保存位置(Uri)
3、在重写的onActivityResult的回调方法中获取拍照所得的图片
4、运行Demo的效果图
总结:这种方法相对于第一种稍微复杂了一点,需要指定图片保存的Uri,不过实现起来也挺容易的。但我们可以通过运行Demo的效果图可以看出相比方法一,获取到的图片就是原图。我们可以通过debug查看获取到图片的尺寸是3104*4192,在我们指定的目录中也可以找到刚才拍照的图片,它的大小在3M左右。最初开始用这种方法获取拍照原图的时候,觉得Google这种设计很不好,为什么不像方法一返回原图的Bitmap对象。后来自己的项目中写了自定义相机才知道为什么不能直接回传原图的Bitmap对象。是因为Activity之间跳转传递数据大小是有限制。一张原图所占的内存是相当大的,比如Demo拍照原图Bitmap对象在代码中所占的大小是3104*4192*4/1024/1024=49.6M。这应该就是Google不直接返回原图Bitmap的原因吧,而且实际开发中我们也不可能直接使用拍照的原图。在开发中都需要对原图进行压缩处理,压缩图片在下面的自定义相机中会说到。
二、自定义相机
在API 21之前Google给开发者提供Camera类来实现自定义相机,但API 21之后Camera就过时了,被一个Camera2类所取代。Demo中自定义相机的实现使用的还是Camera类。Camera2还没有研究过。因为自定义相机的代码太多,我会贴出一些关键的代码,文章底部会给出代码github的地址。写Demo时我尽可能写所有注释。
API提供的自定义相机的步骤
1、获取相机(Camera)的实例,通过open(int),参数是摄像头的id(前、后摄像头)
2、获取相机的默认参数
3、设置相机相关的参数 比如:预览尺寸、拍照图片的尺寸、图片的格式
4、设置屏幕的旋转方向,确保预览方向正确
5、重要:关于相机的预览,Google给出的是使用SurfaceView进行预览。给SurfaceView的SurfaceHold添加callback,就可以获取到SurfaceHold及其SurfaceView的宽高。也就是我们需要设置的预览尺寸。必须先设置SurfaceHold,否则相机无法启动预览。
6、重要:调用startPreview()开始预览,先开始预览才能获取图片。
7、调用takePicture(...)方法进行拍照,回调中可以获取到图像数据了。
8、拍照后将停止预览,如果需要拍照多张就需要重新开始预览。
9、停止预览
10、非常重要:释放相机资源,在onPause()中调用release()、在onResume()中重新打开。(如果没有及时释放相机资源会导致其他地方无法使用相机、而且非常的耗电)
实际开发中自定义相机还是比较复杂的,不仅仅上面这几个步骤和要求。我还要考虑到缩放、聚焦、手机的旋转的方向、转换摄像头、图片压缩等。了解这些之后我们先看一下我的Demo的项目结构,以及每个类的作用。
Demo关键类介绍
CameraManager是一个单例类,是相机的管理类。主要是相机的初始化、参数的设置、拍照等。
CameraUtils是相机工具类,主要是进行一下计算,比如预览尺寸、图片尺寸、手机旋转的方向、聚焦点等。
CameraView就是自定义相机的界面View,主要是相机预览(SurfaceView)、用户的交互(拍照、取消、重拍、确定)以及用户手势的监听和处理(缩放、聚焦、转换摄像头)还有利用重力感应判断手机的旋转方向等。
CaptureLayout是拍照相关操作的界面的布局,写这个是为了不使CameraView过于臃肿,CameraView中进行逻辑处理、UI的操作在该布局中处理。
FocusView是一个自定义VIew,是显示当前聚焦点的View。
CameraManager
1 、打开相机
2、拍照
主要就是这两个方法,还有其他比如设置SurfaceHold、设置缩放倍数、设置聚焦点、转换摄像头、设置手机的旋转方向等就不贴出来了。这些方法都比较简单容易理解。
CameraView
该View继承子FrameLayout,包含预览的SurfaceView、拍照后图片展示的ImageView、拍照相关的按钮布局等。
1、SurfaceView状态的监听,根据SurfaceView的状态来开启/关闭相机
2、和用户的交互
CaptureLayout
1、布局文件
这个类和Camera一点关系都没有,只是拍照界面的 取消、拍照、重拍、确认几个按钮的展示以及和用户的交互。没有难懂的地方。
效果图
总结
主要就是这么多,文章中只贴出来了关键的代码,要真正理解、使用自定义相机还需要去github上将Demo克隆或者下载下来。结合相机(Camera)的API来看代码就会真正懂得自定义相机。
第一次写博客,虽然写的不是很好,但我开始尝试写了。在写的过程中技术是第一个难点,之后就是如何将这些技术问题直接明了的描述清楚也是很重要的。