自定义相机CameraView与踩坑记录
记录下在做自定义CameraView相机功能的过程中的思路和遇到的问题以及解决方案,用的是Camera1 API(考虑到Camera2在部分手机上支持还是比较弱的)
知识储备
没有接触过Camera API的同学强烈建议阅读下面的文章
Android: Camera相机开发详解(上) —— 知识储备
Android: Camera相机开发详解(中) ——实现预览、拍照、保存照片等功能 代码实现过程中也有借鉴过这篇文章
实现思路
- 在xml布局中定义一个SurfaceView,用于预览相机采集的数据
- 给SurfaceHolder添加回调,在surfaceCreated(holder: SurfaceHolder?)回调中打开相机
- 成功打开相机后,设置相机参数。比如:对焦模式,预览大小,照片保存大小等等
- 设置相机预览时的旋转角度,然后调用startPreview()开始预览
- 通过Camera的setOneShotPreviewCallback回调,获取字节数组进行解析成Bitmap
调用takePicture方法拍照 或者 是在Camera的预览回调中 保存照片 - 对保存的照片进行旋转处理,使其为"自然方向"
- 关闭页面,释放相机资源
与Android: Camera相机开发详解(中)不同的是第5点,其余大致思路基本相同
具体实现步骤
一、申请权限
这里用到了前置摄像头,自动对焦,闪关灯
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature
android:name="android.hardware.camera"
android:required="true" />
<uses-feature
android:name="android.hardware.camera.autofocus"
android:required="true" />
<uses-feature
android:name="android.hardware.camera.front"
android:required="true" />
<uses-feature
android:name="android.hardware.camera.flash"
android:required="true" />
二、自定义View用来做Camera的预览View
这里也不同于Android: Camera相机开发详解(中),上文是在布局文件中写了一个SurfaceView来操作,这样虽然也能成功开启预览,但是在这种实现方式下开启预览之后手机按home键SurfaceView就黑屏了,预览就再也起不来了。所以这是第一个坑
解决思路是:将SurfaceView单独抽出来,布局文件中写一个FrameLayout(或其他Layout)的容器,在Activity/Fragment中new这个SurfaceView出来addView到容器中。
谷歌官方的指引中也是这样的写法:
camera-preview
preview-layout
class CameraPreview(private val activity: Activity, context: Context?) : SurfaceView(context),
SurfaceHolder.Callback {
//Camera对象
private var mCamera: Camera? = null
//Camera对象的参数
private lateinit var mParameters: Camera.Parameters
//SurfaceHolder对象
private var mSurfaceHolder: SurfaceHolder = holder
//摄像头方向
var mCameraFacing = Camera.CameraInfo.CAMERA_FACING_BACK
var mFlashStatus = Camera.Parameters.FLASH_MODE_OFF
//预览旋转的角度
var mDisplayOrientation: Int = 0
var isPreviewing = false
init {
mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS)
mSurfaceHolder.addCallback(this)
}
override fun surfaceChanged(holder: SurfaceHolder?, format: Int, width: Int, height: Int) {
}
override fun surfaceDestroyed(holder: SurfaceHolder?) {
releaseCamera()
}
override fun surfaceCreated(holder: SurfaceHolder?) {
openCameraWithFacing()
}
}
CameraPreview在创建后走init初始化,初始化的时候就对holder设置回调mSurfaceHolder.addCallback(this)
,在surfaceCreated中打开相机,设置参数等一系列的操作
/**
* 开启相机并进行预览
*/
private fun openCameraWithFacing() {
val supportCameraFacing = CameraUtil.isSupportCameraFacing(mCameraFacing)
if (supportCameraFacing) {
mCamera = getCameraInstance(mCameraFacing)
if (mCamera == null) {
CameraLogger.log("相机实例获取失败")
return
}
CameraLogger.log("相机实例获取成功")
initParameters(mCamera!!)
startPreview()
} else {
CameraLogger.log("不支持的相机摄像头:$mCameraFacing")
}
}
/**
* 获取相机实例
*/
private fun getCameraInstance(cameraFacing: Int): Camera? {
return try {
Camera.open(cameraFacing) // attempt to get a Camera instance
} catch (e: Exception) {
// Camera is not available (in use or does not exist)
null // returns null if camera is unavailable
}
}
需要注意的是设置参数的initParameters
方法,一开始的是写法是这样的,简单粗暴
private fun initParameters(camera: Camera) {
try {
mParameters = camera.parameters
mParameters.previewFormat = ImageFormat.NV21 //设置预览图片的格式
//获取与指定宽高相等或最接近的尺寸
//设置预览尺寸
val bestPreviewSize = getBestSize(mSurfaceView.width, mSurfaceView.<