显示摄像头预览
连接实时预览
显示摄像头预览 是 CoddyKit 上的免费 Android Academy 课时。 这是第 2 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Android Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Android Academy 课程共包含 4 节课。
目标:实时预览
几乎所有相机应用首先需要的功能都是实时预览——实时显示相机所看到的画面。在本课中,您将构建一个全屏显示相机画面的 Compose 屏幕。
您将组合三个部分:PreviewView、Preview 用例和已绑定的 ProcessCameraProvider。
请求相机权限
在显示预览之前,您需要获得用户的权限。在 Compose 中,Accompanist(或官方权限 API)配合 rememberPermissionState 可以轻松完成这项工作。
只有在权限状态为 granted 后,才启动相机。
@OptIn(ExperimentalPermissionsApi::class)
@Composable
fun CameraScreen() {
val permission = rememberPermissionState(Manifest.permission.CAMERA)
LaunchedEffect(Unit) { permission.launchPermissionRequest() }
if (permission.status.isGranted) {
CameraPreview()
} else {
Text("Camera permission is required.")
}
}在 Compose 中嵌入 PreviewView
PreviewView 是传统的 Android View,因此您需要使用 AndroidView 将它放入 Compose 中。factory lambda 会创建一次视图,并为您提供一个可以配置的引用。
请保存创建的视图,以便将相机表面连接到它。
@Composable
fun CameraPreview(modifier: Modifier = Modifier) {
AndroidView(
modifier = modifier.fillMaxSize(),
factory = { context ->
PreviewView(context).apply {
scaleType = PreviewView.ScaleType.FILL_CENTER
}
}
)
}获取 LifecycleOwner
要绑定相机,您需要一个 LifecycleOwner。在 Compose 中,您可以使用 LocalLifecycleOwner.current 读取当前的生命周期所有者,并使用 LocalContext.current 获取上下文。
设置相机 provider 和绑定用例时,这两者都是必需的。
val context = LocalContext.current
val lifecycleOwner = LocalLifecycleOwner.current构建预览用例
Preview 用例会将帧流式传输到一个表面。构建完成后,您可以调用 setSurfaceProvider,将视图的表面 provider 传给它,从而把预览连接到 PreviewView。
正是这一步连接让相机画面真正显示在屏幕上。
val preview = Preview.Builder().build()
preview.setSurfaceProvider(previewView.surfaceProvider)在协程中进行绑定
获取 provider 是异步操作,因此应在协程中完成。使用与视图关联的 LaunchedEffect,让代码只运行一次。在其中等待 provider,解除之前的所有绑定,然后绑定预览。
LaunchedEffect(previewView) {
val cameraProvider = ProcessCameraProvider.getInstance(context).await()
val preview = Preview.Builder().build().also {
it.setSurfaceProvider(previewView.surfaceProvider)
}
cameraProvider.unbindAll()
cameraProvider.bindToLifecycle(
lifecycleOwner,
CameraSelector.DEFAULT_BACK_CAMERA,
preview
)
}await() 扩展函数
ProcessCameraProvider.getInstance() 会返回一个 ListenableFuture。kotlinx-coroutines-guava 提供的 await() 扩展函数会一直挂起,直到结果就绪,从而将回调代码转换为整洁的顺序代码。
请添加依赖项 org.jetbrains.kotlinx:kotlinx-coroutines-guava 来使用它。
import kotlinx.coroutines.guava.await
// Without await you'd use a listener:
// future.addListener({ val provider = future.get() }, executor)
// await() makes it a single suspend call instead.整合起来
下面是完整的预览可组合项。请注意其流程:在 factory 中创建 PreviewView,获取它的引用,然后在 LaunchedEffect 中进行绑定。
@Composable
fun CameraPreview(modifier: Modifier = Modifier) {
val context = LocalContext.current
val lifecycleOwner = LocalLifecycleOwner.current
val previewView = remember { PreviewView(context) }
LaunchedEffect(previewView) {
val provider = ProcessCameraProvider.getInstance(context).await()
val preview = Preview.Builder().build().also {
it.setSurfaceProvider(previewView.surfaceProvider)
}
provider.unbindAll()
provider.bindToLifecycle(
lifecycleOwner,
CameraSelector.DEFAULT_BACK_CAMERA,
preview
)
}
AndroidView(
factory = { previewView },
modifier = modifier.fillMaxSize()
)
}缩放类型
相机传感器的宽高比很少与屏幕完全匹配。PreviewView.ScaleType 用于控制画面如何适应视图:
FILL_CENTER——填满视图并裁剪边缘(最常用)。FIT_CENTER——显示完整画面,并在两侧或上下留下黑边。
要实现沉浸式全屏相机,请选择 FILL_CENTER。
previewView.scaleType = PreviewView.ScaleType.FILL_CENTER
// or
previewView.scaleType = PreviewView.ScaleType.FIT_CENTER切换相机
要在前置和后置相机之间切换,请将选择器存储在状态中,并在它发生变化时重新绑定。由于 LaunchedEffect 会在键发生变化时重新运行,只需将选择器作为键传入即可。
var selector by remember { mutableStateOf(CameraSelector.DEFAULT_BACK_CAMERA) }
LaunchedEffect(selector) {
val provider = ProcessCameraProvider.getInstance(context).await()
provider.unbindAll()
provider.bindToLifecycle(lifecycleOwner, selector, preview)
}
// Flip button toggles 'selector' between front and back.常见问题
以下两个错误会导致预览画面变黑:
- 忘记调用
setSurfaceProvider——帧没有传输目标。 - 重新绑定前没有调用
unbindAll()——如果同一个用例被绑定两次,CameraX 会抛出错误。
还请记住:只有在权限实际获得后,预览才会启动。
快速检查
检查您对连接预览过程的理解。
回顾
您已经在 Compose 中构建了实时相机预览:
- 启动相机前请求
CAMERA权限。 - 使用
AndroidView承载PreviewView。 - 在协程中等待
ProcessCameraProvider。 - 构建
Preview,调用setSurfaceProvider和unbindAll(),然后调用bindToLifecycle。
接下来,您将添加 ImageCapture 用例并拍摄真实照片。
常见问题解答
「显示摄像头预览」课时是免费的吗?
是的 — 「显示摄像头预览」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Android Academy 课程的其余内容,请升级到 CoddyKit PRO。 Android Academy 课程共包含 4 节课。
「显示摄像头预览」这节课中我会学到什么?
连接实时预览 你通过在浏览器中直接运行的动手代码来练习 Android Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Android Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Android Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 2 节课,共 4 节。
「显示摄像头预览」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Android Academy 课中编写并运行代码吗?
能。每节 Android Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- CameraX 概览
- 显示摄像头预览
- 拍照
- 录制视频