轻松实现 Android 相机功能:使用 CameraX 构建简单应用
前言
在智能手机不断进化的今天,影像功能已成为消费者选择手机时的重要考量因素之一。各大手机厂商也越来越注重提升手机的影像表现。从最初的简单拍照到如今的专业级摄影体验,手机相机的性能和图像处理技术都取得了飞跃性的发展。如今的智能手机不仅能拍摄高质量的照片,还能录制 4K 视频、实现人脸识别、进行实时图像分析,甚至通过增强现实(AR)技术提供更加丰富的用户体验。各大厂商也推出了各具特色的影像技术,例如:小米的徕卡影像、华为的红枫影像、vivo 的蔡司镜头、以及 OPPO 的哈苏影像。这些先进的功能背后,都离不开强大的相机开发框架与库的支持。
然而,随着不同设备的涌现以及硬件差异和操作系统版本的不断更新,开发者在实现相机功能时面临着巨大的挑战。传统的 Android 相机 API 不仅复杂,而且难以适配各种设备,开发者往往需要耗费大量时间进行底层配置与优化。为了解决这些问题,Google 推出了 CameraX——一个专为 Android 开发者设计的相机库。CameraX 以其简洁、高效和跨设备适配的优势,为开发者提供了更为一致、易用的相机功能实现方案。
一、cameraX概述
1、 cameraX的简介
CameraX 是 Google 推出的一个 Android 相机库,旨在简化相机功能的开发,提升跨设备兼容性,并提供更加一致的 API 接口。与传统的 Android 相机 API 相比,CameraX 更加易于使用,它通过封装底层的相机硬件操作,使开发者能够专注于实现业务逻辑和用户体验,而不必过多关心设备差异、性能优化和权限管理等复杂问题。
CameraX 提供了多个模块,包括 相机预览(Preview)、拍照(ImageCapture)、视频录制(VideoCapture) 以及 图像分析(ImageAnalysis) 等,覆盖了大多数常见的相机应用场景。同时,CameraX 自动适配不同型号的设备,确保即便是在硬件配置差异较大的 Android 设备上,应用依然能够流畅运行。
该库与 Android Jetpack 无缝集成,能够与 Lifecycle 和 Permissions 库一起使用,帮助开发者更方便地管理相机生命周期和权限请求。此外,CameraX 还支持与其他 Google 库(如 ML Kit)配合,进行实时图像分析和机器学习应用,从而开辟了更广阔的应用场景。
2、cameraX的结构
在使用 CameraX时,提供的用例如下:
预览:接受用于显示预览的 Surface,例如 PreviewView。
图片分析:为分析(例如机器学习)提供 CPU 可访问的缓冲区。
图片拍摄:拍摄并保存照片。
视频拍摄:通过 VideoCapture 拍摄视频和音频
3、API模型
当我们使用cameraX时,需要指定以下内容:
- 具有配置选项的所需用例。
- 通过附加监听器来指定如何处理输出数据。
- 通过将用例绑定到 Android 架构生命周期来指定目标流程,例如何时启用相机及何时生成数据。
我们可以采用两种方式编cameraX应用:CameraController(最简单应用cameraX的方式)或 CameraProvider(具有更高的灵活性)。
CameraController
CameraController 在单个类中提供大多数 CameraX 核心功能。它只需少量设置代码,并且可自动处理相机初始化、用例管理、目标旋转、点按对焦、双指张合缩放等操作。扩展 CameraController 的具体类为 :
LifecycleCameraController。(Kotlin)
// 获取 PreviewView 的引用,PreviewView 是用来显示相机预览的视图组件
val previewView: PreviewView = viewBinding.previewView
// 创建 LifecycleCameraController 实例,它是 CameraX 提供的一个简化的相机控制器
// 通过它可以方便地管理相机的生命周期和预览
var cameraController = LifecycleCameraController(baseContext)
// 将相机控制器与当前的生命周期绑定,通常是 Activity 或 Fragment。
// 这样相机的生命周期会自动跟随组件的生命周期进行管理,
// 当生命周期结束时,CameraX 会自动释放相机资源
cameraController.bindToLifecycle(this)
// 设置相机控制器使用后置摄像头,CameraSelector 用来选择前置或后置摄像头
cameraController.cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
// 将相机控制器绑定到 PreviewView 上,
// 这样相机的预览内容就会显示在 previewView 中,用户可以看到实时的相机画面
previewView.controller = cameraController
CameraController 的默认 UseCase 为 Preview、ImageCapture 和 ImageAnalysis。如需关闭 ImageCapture 或 ImageAnalysis,或者开启 VideoCapture,请使用 setEnabledUseCases() 方法。
CameraProvider
CameraProvider 仍然易于使用,但由于应用开发者会处理更多设置,因此有更多机会自定义配置,例如在 ImageAnalysis 中启用输出图片旋转或设置输出图像格式。我们还可以使用自定义 Surface 进行相机预览以提高灵活性,而对于 CameraController,需要使用 PreviewView。如果现有的 Surface 代码已是应用的其他部分的输入,则使用该代码会更加高效。
我们可以采用 set() 方法配置用例,并使用 build() 方法完成这些用例。每个用例对象都提供一组特定于该用例的 API。例如,图片拍摄用例会提供 takePicture() 方法调用。
应用没有在 onResume() 和 onPause() 中放置具体的启动和停止方法调用,而是使用 cameraProvider.bindToLifecycle() 指定要与相机关联的生命周期。之后,该生命周期会告知 CameraX 何时配置相机拍摄会话并确保相机状态随生命周期的转换相应地变化。
如需了解每个用例的实现步骤,请参阅实现预览、分析图片、图片拍摄和视频拍摄
预览用例会与 Surface 互动以展示预览。应用使用以下代码创建具有配置选项的用例:
// 创建 Preview 实例,用于显示相机的预览画面
val preview = Preview.Builder().build()
// 获取布局文件中的 PreviewView 控件,用于呈现相机的实时预览画面
val viewFinder: PreviewView = findViewById(R.id.previewView)
// 将相机的使用场景绑定到 Android 的生命周期管理中
// 这样相机的生命周期将与 lifecycleOwner(例如 Activity 或 Fragment)同步管理
val camera = cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview)
// 使用 PreviewView 来创建 SurfaceProvider,它是显示相机预览的推荐方式
// 通过该方法将相机预览绑定到 PreviewView
preview.setSurfaceProvider(viewFinder.getSurfaceProvider())
4、要求
CameraX 具有以下最低版本要求:
Android API 级别 21
Android 架构组件 1.1.1
二、如何构建一个基于cameraX的简单相机
1. 创建项目
1、 在 Android Studio 中,创建一个新项目,并在出现提示时选择“Empty Activity”。
2、接下来,将应用命名为“CameraXApp”,然后确认软件包名称或将其更改为“com.android.example.cameraxapp”。选择 Kotlin 作为语言,并将最低 API 级别设置为 21(CameraX 所需的最低级别)。
添加gradle依赖
1、打开 CameraXApp.app 模块的 build.gradle 文件,并添加 CameraX 依赖项:
dependencies {
def camerax_version = "1.1.0-beta01"
implementation "androidx.camera:camera-core:${camerax_version}"
implementation "androidx.camera:camera-camera2:${camerax_version}"
implementation "androidx.camera:camera-lifecycle:${camerax_version}"
implementation "androidx.camera:camera-video:${camerax_version}"
implementation "androidx.camera:camera-view:${camerax_version}"
implementation "androidx.camera:camera-extensions:${camerax_version}"
}
2、CameraX 需要一些属于 Java 8 的方法,因此我们需要相应地设置编译选项。在 android 代码块的末尾,紧跟在 buildTypes 之后,添加以下代码:
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
3、此 Codelab 使用 ViewBinding,因此请使用以下代码(在 android{} 代码块末尾)启用它:
buildFeatures {
viewBinding true
}
创建 Codelab 布局
1、在此 Codelab 的界面中,我们使用了以下内容:
- CameraX PreviewView(用于预览相机图片/视频)。
- 用于控制图片拍摄的标准按钮。
- 用于开始/停止视频拍摄的标准按钮。
- 用于放置 2 个按钮的垂直指南。
我们将默认布局替换为以下代码,从而:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.camera.view.PreviewView
android:id="@+id/viewFinder"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<Button
android:id="@+id/image_capture_button"
android:layout_width="110dp"
android:layout_height="110dp"
android:layout_marginBottom="50dp"
android:layout_marginEnd="50dp"
android:elevation="2dp"
android:text="@string/take_photo"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintEnd_toStartOf="@id/vertical_centerline" />
<Button
android:id="@+id/video_capture_button"
android:layout_width="110dp"
android:layout_height="110dp"
android:layout_marginBottom="50dp"
android:layout_marginStart="50dp"
android:elevation="2dp"
android:text="@string/start_capture"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@id/vertical_centerline" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/vertical_centerline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent=".50" />
</androidx.constraintlayout.widget.ConstraintLayout>
布局效果:
2、更新res/value/sstrings.xml资源文件:
<resources>
<string name="app_name">CameraXApp</string>
<string name="take_photo">Take Photo