Android Camera各个API拍照实践
前言
好久没更新博客了,过了年基本就没怎么动了,不过还是做了一些东西,最近有时间觉得还是得写一写,不然过段时间就忘了,不划算。
最近把Android Camera的三种API一一试了下,实现了拍照和录像,和图片、bitmap相关的功能也练习了下,比如获取、保存、删除图片等。
ps. 录像的文章也写好了,有兴趣可以继续看下:
下面就来记录下Android三种API的拍照实践。
目标
先来明确下目标,即要做到的效果:
- 能够使用Android三种API预览、拍照、显示结果: Camera1、Camera2、CameraX
- 三种API能够自由切换,互不干扰
- 能够拿到拍照结果,并对图片进行一些操作,如保存、删除等
还是比较简单的,下面就开干。
使用Camera1 API
接口封装
在一段摸索后,我先抽象了一个接口,用来统一三种API的行为:
1 | kotlin复制代码import android.graphics.Bitmap |
其实就三个方法,预览、拍照、释放资源,这里我传入了ComponentActivity来获取context,也方便用它的lifecycleScope来使用协程。因为拍照预览可以用SurfaceView、TextureView、PreviewView这几个,我这直接设置成了泛型,看情况用吧。
封装好接口,我们就一步一步实现功能了。
Camera1预览
使用Camera1 API,必须先预览才能拍照,其他API倒没有要求。我这用了SurfaceView来预览,TextureView也可以,下面看下startPreview的写法:
1 | kotlin复制代码/** |
主要就是这七步,首先要根据摄像头类型获得cameraId,再根据cameraId去打开摄像头,这时候摄像头的方向默认是横着的,还得改下摄像头方向,详细的代码后面会完整提供,现在大致看下流程,
参数设置特别要注意下,这里的width要比height更大,而且要选对尺寸,不然会造成各种意想不到的问题,后面几个API也是一样:
1 | kotlin复制代码private fun setCameraParameters(camera: Camera) { |
另外一个需要注意的就是mSurfaceCallback,正常使用的话,应该在mSurfaceCallback的surfaceCreated里面去open Camera的,不过我这写成了工具类,调用的时候收不到surfaceCreated,而是已经created了,所以不处理mSurfaceCallback,直接open,当然这里也要根据实际情况去处理。
再一个就是在startPreview前,一定要调用setPreviewDisplay,传入SurfaceView的holder,再通过mCamera去startPreview就可以预览了,预览的时候确保下SurfaceView处于VISIBLE状态,预览就完成了。
Camera1拍照
搞定预览后,拍照功能其实就完成的差不多了,通过mCamera去takePicture就行了
1 | kotlin复制代码/** |
这里传出的bitmap,并且调整了下方向,没什么好说的。
Camera1释放资源
只会写拍照,没什么好说的,要能正确释放资源,才是一个好程序员,下面看下释放资源:
1 | kotlin复制代码/** |
完整代码
上面讲了个大概,下面提供完整代码,加了个takePhotoNoFeeling无感拍照和continuePreview,使用Camera1拍照后,需要调用startPreview继续预览才能再拍照,需要注意下。
(代码有点长,还是去GitHub看吧)
使用Camera2 API
Camera1被标记过时了,Google推荐使用CameraX,CameraX其实用的也是Camera2,只不过Camera2比较难用,但是学习嘛,总得试试,就仿照上面Camera1的写法,下面看下Camera2的使用。
Camera2预览
Camera1中我们用了SurfaceView,这里就来用下TextureView,关于两者区别可以查下资料,这里不详叙,下面看预览代码:
1 | kotlin复制代码/** |
其实吧,和Camera1类似,都要选择相机得到mCameraId,再开启相机,只不过Camera2预览的时候,要要创建Session对话,还要把要输出的surface全部传进去,而且图片要通过ImageReader去读取,这么一搞真就复杂多了。
这里要注意下创建的session,要把预览及ImageReader的surface都传进去,不然就出错了。
1 | kotlin复制代码// 5.创建Capture Session |
另外,这两者的尺寸也要匹配下,我这两个都是用的最大尺寸:
1 | kotlin复制代码val map = info.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP) |
和mSurfaceCallback类似,Camera2的openCamera也应该在mTextureViewCallback的onSurfaceTextureAvailable中调用,只不过我这写成工具类,收不到这个回调,直接就用了。
因为这里有很多回调,我这用了协程和suspend方法,不是必须,只不过能让代码结构更清晰。
Camera2的preview是通过发送重复的请求实现的,其实可以持有previewRequestBuilder,在过程中修改配置,达到想要的效果:
1 | kotlin复制代码private fun preview(surface: Surface) { |
Camera2拍照
看到上面Camera2的预览就挺麻烦了,结果Camera2的拍照也比Camera1来的麻烦:
1 | kotlin复制代码/** |
这里需要用captureRequestBuilder创建拍照请求,设置好参数,最后通过mSession去拍照,这里能拿到TotalCaptureResult,里面有很多数据,只不过我只想拿bitmap,所以要去mImageReader获取。
Camera2释放资源
Camera2涉及的东西更多,释放资源也更复杂些,需要注意下。
1 | kotlin复制代码override fun release() { |
完整代码
需要注意Android 5.0以后版本才能使用Camera2 API。
Camera2CaptureHelper
使用CameraX API
Camera1 API简单但是过时了,Camera2 API功能强大,使用起来却十分复杂,还好Google在JetPack中提供了CameraX,方便我们使用Camera的相关功能,下面就俩看看吧。
引入CameraX库
CameraX作为JetPack的库,还是需要我们引入库的,我这用的version catalog管理依赖,实际都差不多,就下面几个库(算是把拍照也引入了):
1 | toml复制代码# cameraX |
这里的cameraX版本并没有用最新的,我试了1.2和1.3版本,需要升级比较高的gradle版本,想想还是算了。
在dependence里面添加上依赖,就能开始写代码了。
1 | kotlin复制代码dependencies { |
CameraX预览
前面我们分别使用了SurfaceView和TextureView进行预览,而在CameraX里面提供了更好的PreviewView来预览,下面看下CameraX如何预览:
1 | kotlin复制代码/** |
果然看起来就舒服多了,和Camera2比起来,简单太多了,就是获取了一个mCameraProvider,设置下preview,然后绑定到activity的生命周期就能预览了,根本不需要怎么解释。
如果要拍照,创建个imageCapture,在bindToLifecycle最后加上就行了,这里还帮我们搞定了异步线程问题。
CameraX拍照
CameraX的预览很简单,拍照就更简单了:
1 | kotlin复制代码/** |
直接通过imageCapture的takePicture拍照,拿到image对象就能获取bitmap了,so easy!
CameraX释放资源
CameraX不用了直接解除生命周期的绑定就行了。
1 | kotlin复制代码/** |
完整代码
使用Demo
我这写了个例子,用了这三个API,还加上了系统拍照、系统选取、系统分享、保存相册等,有兴趣可以参考下:
Demo地址:
小结
花了点时间,把Android相机中Camera1、Camera2、CameraX三个API的拍照功能实践了下,并编写成工具类方便使用。
本文转载自: 掘金