(原创)Jetpack系列(四):Camerax

本文深入探讨了Camerax在Android应用开发中的作用,详细介绍了其简化相机应用开发的优势及其实现复杂相机功能的方法。从预览、图像分析到图片拍摄,文章提供了丰富的示例代码,并总结了使用Camerax时应注意的关键点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

在 Android 应用中要实现 Camera 功能还是比较困难的,

为了保证在各品牌手机设备上的兼容性、响应速度等体验细节,

Camera 应用的开发者往往需要花很大的时间和精力进行测试,

甚至需要手动在数百种不同设备上进行测试。

简单的拍照功能,可以直接调用系统相机

但是当面对一些复杂的Camera页面定制时

小型开发团队往往显得束手无策

CameraX 正是为解决这个痛点而诞生的。

这篇文章,就来探索下谷歌jetpack库里的Camerax

看下它是如何帮助开发者更好地定制相机应用的

Camerax是什么

来自谷歌官方文档的介绍:

CameraX 是一个 Jetpack 支持库,

旨在帮助您简化相机应用的开发工作。

它提供一致且易用的 API 接口,适用于大多数 Android 设备,并可向后兼容至 Android 5.0(API 级别 21)。

虽然 CameraX 利用了 camera2 的功能,

但采取了一种具有生命周期感知能力且基于用例的更简单方式。

它还解决了设备兼容性问题,因此您无需在代码库中添加设备专属代码。

这些功能减少了将相机功能添加到应用时需要编写的代码量。

最后,借助 CameraX,开发者只需两行代码就能实现与预安装的相机应用相同的相机体验和功能。

CameraX Extensions 提供了一些可选的插件,让您可以在支持的设备上添加各种特效,例如人像、HDR、夜间和美颜模式。

关于Camerax支持的设备,可以参考官方文档的Camerax设备页
Camerax支持设备

Camerax优点和使用场景

1、CameraX 和 Lifecycle 结合在一起,方便开发者管理生命周期。且相比较 camera2 减少了大量样板代码的使用。

2、CameraX 是基于 Camera2 API 实现的,兼容至 Android L (API 21),从而确保兼容到市面上绝大多数手机

3、开发者可以通过扩展的形式使用和原生摄像头应用同样的功能(如:人像、夜间模式、HDR、滤镜、美颜)

4、Google 自己还打造了 CameraX 自动化测试实验室,对摄像头功能进行深度测试,确保能覆盖到更加广泛的设备。相当于 Google 帮我们把设备兼容测试工作给做了。

Camerax的功能,可以从下面几个方向研究

预览:在屏幕上显示图像

图像分析:无缝访问缓冲区中的图像以便在算法中使用,例如将其传入 MLKit

图片拍摄:保存优质图片

一般图像分析用的较少,要实现自定义相机

使用里面预览和拍摄功能即可

Camerax基础使用

初始化预览

初始化之前,记得检查拍照权限

//初始化相机
    private fun initCamera(lensFacing: Int = CameraSelector.LENS_FACING_BACK) {

        val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
        cameraProviderFuture.addListener(Runnable {

            try {
                cameraProvider = cameraProviderFuture.get()

                preview = Preview.Builder().build()


                //配置参数(后置摄像头等)注释的是自己配置的,其实系统提供了
                //CameraSelector.DEFAULT_FRONT_CAMERA
                //CameraSelector.DEFAULT_BACK_CAMERA
                //两个默认的配置方法(一个是前置摄像头,一个是后置摄像头)
                // Choose the camera by requiring a lens facing
//                cameraSelector =
//                    CameraSelector.Builder()
//                        .requireLensFacing(lensFacing)
//                        .build()
                if (lensFacing == CameraSelector.LENS_FACING_FRONT) {
                    cameraSelector = CameraSelector.DEFAULT_FRONT_CAMERA
                } else {
                    cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
                }

                //图片拍摄用例
                //如需缩短照片拍摄的延迟时间,
                // 请将 ImageCapture.CaptureMode 设置为 CAPTURE_MODE_MINIMIZE_LATENCY。
                // 如需优化照片质量,请将其设置为 CAPTURE_MODE_MAXIMIZE_QUALITY。
                mImageCapture = ImageCapture.Builder()
                    .setFlashMode(ImageCapture.FLASH_MODE_AUTO)//闪光灯
//                    .setCaptureMode(ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY)
                    .build()


                //指定要与相机关联的生命周期,该生命周期会告知 CameraX 何时配置相机拍摄会话并确保相机状态随生命周期的转换相应地更改。
                camera = cameraProvider.bindToLifecycle(
                    this,
                    cameraSelector,
                    preview,
                    mImageCapture
                )
                //初始化缩放两倍
//                camera.cameraControl.setZoomRatio(2f)


                //相机预览
                preview?.setSurfaceProvider(view_finder.getSurfaceProvider())
            } catch (e: java.lang.Exception) {
                Log.d("MainActivity", "初始化相机错误" + e.message)
                e.printStackTrace()
            }


        }, ContextCompat.getMainExecutor(this))


    }

这里主要要认识五个类,他们的功能如下

private lateinit var mImageCapture: ImageCapture//执行拍照,以及设置闪光灯

private lateinit var cameraSelector: CameraSelector//得到和修改前置/后置摄像头

private lateinit var camera: Camera//相机信息类,包括聚焦和缩放比例

private lateinit var cameraProvider: ProcessCameraProvider//指定要与相机关联的生命周期

private lateinit var preview: Preview//相机预览

xml布局里用到的view_finder预览控件为

<androidx.camera.view.PreviewView/>

拍照

拍照需要用到mImageCapture类,同时可以用这个类设置是否打开闪光灯

拍照得到的图片,可以存在本地,或者内存缓存中

存本地的话传入一个File文件即可

private fun takePhoto() {
    if (mImageCapture != null) {
        //播放系统拍照的声音
        MediaActionSound().play(MediaActionSound.SHUTTER_CLICK)
        val cretaeFile = cretaeFile()
        val outputFileOptions: ImageCapture.OutputFileOptions =
            ImageCapture.OutputFileOptions.Builder(cretaeFile).build()
        //public void takePicture(@NonNull Executor executor,
        //            final @NonNull OnImageCapturedCallback callback)
        //还有一个重载方法,可以将拍到的图片缓存在内存里,
        // 一般不做本地保存,只是展示的话,可以用这个
        mImageCapture?.takePicture(
            outputFileOptions,
            ContextCompat.getMainExecutor(this),
            object : ImageCapture.OnImageSavedCallback {
                override fun onImageSaved(@NonNull outputFileResults: ImageCapture.OutputFileResults) {
                    Log.d("MainActivity", "absolutePath:${cretaeFile.absolutePath}")
                    btn_takephone.text = "保存成功"
                    btn_takephone.postDelayed(Runnable {
                        btn_takephone.text = "拍照"
                    }, 1000)
                }

                override fun onError(@NonNull exception: ImageCaptureException) {
                    btn_takephone.text = "保存失败"
                }
            }

        )
    }
}

设置闪光灯

mImageCapture.flashMode

可以设置三种状态

打开 FLASH_MODE_ON

关闭 FLASH_MODE_OFF

自动 FLASH_MODE_AUTO

设置前置/后置摄像头

得到当前摄像头状态是前置还是后置的变量为:

cameraSelector.lensFacing

一共两种状态

前置 CameraSelector.LENS_FACING_FRONT

后置 CameraSelector.LENS_FACING_BACK

修改前置/后置摄像头

需要重新设置相机的预览

以及解除生命周期的绑定

cameraProvider.unbind(preview, mImageCapture)

然后重新调用initCamera方法对相机初始化即可

设置聚焦

根据触摸时间的监听

得到点击的触点的x,y坐标然后设置焦聚焦


// 创建 MeteringPoint,命名为 factory
val meteringPointFactory = view_finder.meteringPointFactory

// 将 UI 界面的坐标转换为摄像头传感器的坐标
val point = meteringPointFactory.createPoint(x, y)

// 创建对焦需要用的 action
val action = FocusMeteringAction.Builder(point).build()

// 执行所创建的对焦 action
camera?.cameraControl?.startFocusAndMetering(action)

设置缩放

根据用户的手势操作

计算出需要缩放的比例

然后设置缩放

// 获取当前的摄像头的缩放比例
                val currentZoomRatio: Float = camera.cameraInfo.zoomState.value?.zoomRatio ?: 1F

                //得到显示界面可以缩放的范围(不知道不同机型会不会不一样,红米k40是1-10)
//                Log.d("MainActivity", "minZoomRatio:"+camera.cameraInfo.zoomState.value?.minZoomRatio)
//                Log.d("MainActivity", "maxZoomRatio:"+camera.cameraInfo.zoomState.value?.maxZoomRatio)


                // 获取用户捏拉手势所更改的缩放比例
                val delta = detector.scaleFactor
//                Log.d("MainActivity", "currentZoomRatio:"+currentZoomRatio)
//                Log.d("MainActivity", "scaleFactor:" + delta)
//                Log.d("MainActivity", "缩放:" + (currentZoomRatio * delta))
                // 更新摄像头的缩放比例,要在缩放范围内,否则是无效的
                camera.cameraControl.setZoomRatio(currentZoomRatio * delta)

一些需要注意的点

1、图片保存在本地时,记得获取存储权限

2、测试用例使用的Camerax版本为:

//以下是CameraX库
// CameraX core library using the camera2 implementation
def camerax_version = "1.0.0-rc04"
// The following line is optional, as the core library is included indirectly by camera-camera2
implementation "androidx.camera:camera-core:${camerax_version}"
implementation "androidx.camera:camera-camera2:${camerax_version}"
// If you want to additionally use the CameraX Lifecycle library
implementation "androidx.camera:camera-lifecycle:${camerax_version}"
// If you want to additionally use the CameraX View class
implementation "androidx.camera:camera-view:1.0.0-alpha23"
// If you want to additionally use the CameraX Extensions library
implementation "androidx.camera:camera-extensions:1.0.0-alpha23"

截止发文时,官方文档已经更新到了

1.0.0-rc05

alpha24

所以可以参考Camerax版本说明

去查看更新的内容

3、现在网上的很多关于Camerax的资料

调用的api都不一样

其实是Camerax更新导致

其中一些方法都已经删除和过时

建议自己阅读时进行实际的代码验证

以确保可靠

4、关于得到相机前置/后置摄像头的方法

cameraSelector.lensFacing

这个方法被加上了注解

@SuppressLint(“RestrictedApi”)

代表是受限制的api

但目前没找到另外一种有效的方式去得到相机摄像头的状态

如果有最新解决方案的,也欢迎交换意见

分享总结和参考资料

因为Camerax更新的频率还是较快的

所以一些api变动也比较大

在整理这些功能的时候

发现只参考官方文档的资料

比较有限

这里还参考了一些原来的博客,文章

通过参考里面的一些实例

得到了在Camerax里面新的api

其中谷歌开发者官方账号发布的文章发挥了较大作用

这里贴出链接地址

使用 PreviewView 来展示相机预览

Camerax概览

Camerax版本说明

最后,Camerax就讲解到这里了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值