相机预览问题
通过SurfaceView,TextureView,GlSurfaceView显示相机预览
显示相机预览内容是每个相机类应用都会包含的功能,想要完美实现这个却并非易事。原因是,在某些特别极端情况下 camera2 API 的使用会变得很复杂,而且在不同设备上的行为还会有所不同。还好, Jetpack CameraX 库 的 PreviewView 可以帮助您解决这一问题。通过在各种 Android 设备上提供开发者友好、一致且稳定的 API,使得展示相机的预览变得不再困难。
如果要向 Android 应用中添加相机功能,您有以下三个主要选项:
CameraX 基于 Camera2 软件包构建而成。如果您需要低级别的相机控件来支持复杂用例,那么 Camera2 是一个不错的选择,但相应 API 比 CameraX 更复杂,并且您需要管理设备专属配置。与 CameraX 一样,Camera2 适用于 Android 5.0(API 级别 21)及更高版本。
CameraX 支持大多数常见的相机用例:
使用SurfaceView加载相机预览界面
1,添加相机权限
<uses-permission android:name="android.permission.CAMERA" />
2,页面布局,因为我项目需要的是一个扫描页面,所以我在底布局下填充了一个SurfaceView
<SurfaceView
android:id="@+id/mSurfaceView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<ImageView
android:id="@+id/view2"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_margin="60dp"
android:background="@drawable/rpa_huli_rzpz_bianjiao_icon"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="h,16:10"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/face_confirm"
android:layout_width="@dimen/public_120_dp"
android:layout_height="@dimen/public_60_dp"
android:layout_marginTop="136dp"
android:background="@color/detail_bu"
android:text="识别"
android:textColor="@color/white"
android:textSize="24sp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/view2" />
3,设置SurfaceHolder.callback开启和关闭相机预览功能。
private val cpHolderCallback: SurfaceHolder.Callback = object : SurfaceHolder.Callback {
override fun surfaceCreated(holder: SurfaceHolder) {
camera?.setPreviewDisplay(holder)
preview()//相机预览
}
override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {}
override fun surfaceDestroyed(holder: SurfaceHolder) {
stopPreview()//关闭预览
}
}
4,初始化相机数据,加载相机预览
runOnUiThread {
camera = Camera.open(0)
try {
val parameters: Camera.Parameters = camera!!.parameters
parameters.pictureFormat = ImageFormat.JPEG//设置图片属性
//设置相机对焦模式,FOCUS_MODE_CONTINUOUS_PICTURE使用连续对焦
parameters.focusMode = Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE
//界面属性不一样,所以设置宽高处理界面异常拉伸
val display = windowManager.defaultDisplay
val height = display.height
val width = display.width
val preSize: Camera.Size =
CameraUtils.getCloselyPreSize(true, width,height,
parameters.supportedPreviewSizes)
parameters.setPictureSize(preSize.width, preSize.height)
camera!!.run {
setParameters(parameters)
setPreviewDisplay(mSurfaceView.holder)
//设置将相机获取到的界面旋转90度显示
setDisplayOrientation(90)
startPreview()
cancelAutoFocus()
}
safeToTakePicture = true
} catch (e: IOException) {
e.printStackTrace()
}
}
5,监听扫描按钮,获取到图片,这里因为是预览界面,所以和平时相机拍照的方法有一些区别,需要使用takePicture函数,三个参数我使用了图片回调,因为我需要得到图片。
face_confirm.setOnClickListener {
camera?.takePicture(null, null, Camera.PictureCallback { data, _ ->
//data是ByteArray类型的,所以需要转换一下才能得到图片临时存放的地址
pictureDataBytes = data
stopPreview()//获取完之后停止相机预览活动
...
}).start()
})
}
6,ByteArray转换String函数,获取到图片的拼接地址。
private fun PictureConversion(bytes: ByteArray?): String? {
try {
val f: File = File.createTempFile("img", ".jpg")
val fos = FileOutputStream(f)
fos.write(bytes)
fos.flush()
fos.close()
return f.getAbsolutePath()
} catch (e: IOException) {
e.printStackTrace()
}
return ""
}