作者 / Google 開(kāi)發(fā)者關(guān)系工程師 Jolanda Verhoef
本文是 "相機(jī)與媒體 Spotlight Week" 系列的內(nèi)容之一。此系列中,我們會(huì)提供文章、視頻、示例代碼等資源,以幫助您提升應(yīng)用中的媒體體驗(yàn)。
我們了解到您喜歡 CameraX 和 Jetpack Compose 庫(kù)提供的強(qiáng)大功能,但希望使用更符合語(yǔ)言習(xí)慣的 Compose API 來(lái)構(gòu)建相機(jī)界面。今年,我們的工程團(tuán)隊(duì)開(kāi)發(fā)了兩個(gè)新的 Compose 工件,即低層級(jí) viewfinder-compose 和高層級(jí) camera-compose。兩者現(xiàn)在均已推出 alpha 版本 。
在本系列文章中,我們不僅將為您介紹如何將 camera-compose API 集成到應(yīng)用中,還將向您展示與 Compose 集成所帶來(lái)的一些令人愉悅的界面體驗(yàn)。所有令人贊嘆的 Compose 功能 (例如自適應(yīng) API 和動(dòng)畫(huà)支持) 均已與相機(jī)預(yù)覽無(wú)縫集成。
完成所有這些操作后,我們的最終應(yīng)用將如下所示:
此外,應(yīng)用可以順暢地在桌面模式之間切換:
到本文 (該系列第一篇文章) 的末尾,您將構(gòu)建一個(gè)功能齊全的相機(jī)取景器,并將在后續(xù)系列文章中對(duì)其進(jìn)行擴(kuò)展。歡迎您跟隨我們一起編寫代碼,在實(shí)踐中更好地學(xué)習(xí)。
添加庫(kù)依賴項(xiàng)
假設(shè)您已經(jīng)在應(yīng)用中設(shè)置了 Compose。如果您想繼續(xù),只需在 Android Studio 中新建一個(gè)應(yīng)用即可。我們通常使用最新的 Canary 版本,因?yàn)檫@個(gè)版本會(huì)提供最新的 Compose 模板。
向您的 libs.versions.toml 中添加以下內(nèi)容:
[versions] .. camerax = "1.5.0-alpha03" accompanist = "0.36.0" # or whatever matches with your Compose version [libraries] .. # Contains the basic camera functionality such as SurfaceRequest androidx-camera-core = { module = "androidx.camera:camera-core", version.ref = "camerax" } # Contains the CameraXViewfinder composable androidx-camera-compose = { module = "androidx.camera:camera-compose", version.ref = "camerax" } # Allows us to bind the camera preview to our UI lifecycle androidx-camera-lifecycle = { group = "androidx.camera", name = "camera-lifecycle", version.ref = "camerax" } # The specific camera implementation that renders the preview androidx-camera-camera2 = { module = "androidx.camera:camera-camera2", version.ref = "camerax" } # The helper library to grant the camera permission accompanist-permissions = { module = "com.google.accompanist:accompanist-permissions", version.ref = "accompanist" }然后,將這些添加到您的模塊 build.gradle.kts 依賴項(xiàng)塊中:
dependencies {
..
implementation(libs.androidx.camera.core)
implementation(libs.androidx.camera.compose)
implementation(libs.androidx.camera.lifecycle)
implementation(libs.androidx.camera.camera2)
implementation(libs.accompanist.permissions)
}
?為了授予相機(jī)權(quán)限,我們添加了所有依賴項(xiàng),然后實(shí)際顯示相機(jī)預(yù)覽。接下來(lái),讓我們看看如何授予正確的權(quán)限。
授予相機(jī)權(quán)限
通過(guò)使用 Accompanist 權(quán)限庫(kù),我們可以輕松地授予正確的相機(jī)權(quán)限。首先,我們需要設(shè)置 AndroidManifest.xml:
..
Accompanist 權(quán)限庫(kù)
https://google.github.io/accompanist/permissions/
現(xiàn)在,我們只需按照庫(kù)的說(shuō)明授予正確的權(quán)限:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
MyApplicationTheme {
CameraPreviewScreen()
}
}
}
}
@OptIn(ExperimentalPermissionsApi::class)
@Composable
fun CameraPreviewScreen(modifier: Modifier = Modifier) {
val cameraPermissionState = rememberPermissionState(android.Manifest.permission.CAMERA)
if (cameraPermissionState.status.isGranted) {
CameraPreviewContent(modifier)
} else {
Column(
modifier = modifier.fillMaxSize().wrapContentSize().widthIn(max = 480.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
val textToShow = if (cameraPermissionState.status.shouldShowRationale) {
// If the user has denied the permission but the rationale can be shown,
// then gently explain why the app requires this permission
"Whoops! Looks like we need your camera to work our magic!" +
"Don't worry, we just wanna see your pretty face (and maybe some cats). " +
"Grant us permission and let's get this party started!"
} else {
// If it's the first time the user lands on this feature, or the user
// doesn't want to be asked again for this permission, explain that the
// permission is required
"Hi there! We need your camera to work our magic!
" +
"Grant us permission and let's get this party started! uD83CuDF89"
}
Text(textToShow, textAlign = TextAlign.Center)
Spacer(Modifier.height(16.dp))
Button(onClick = { cameraPermissionState.launchPermissionRequest() }) {
Text("Unleash the Camera!")
}
}
}
}
@Composable
private fun CameraPreviewContent(modifier: Modifier = Modifier) {
// TODO: Implement
}
這樣,我們就得到了一個(gè)良好的界面,用戶可以在顯示相機(jī)預(yù)覽之前授予相機(jī)權(quán)限:創(chuàng)建 ViewModel
將業(yè)務(wù)邏輯與界面分開(kāi)是一種很好的實(shí)踐。為此,我們可以為屏幕創(chuàng)建視圖模型來(lái)實(shí)現(xiàn)這一點(diǎn)。這個(gè)視圖模型設(shè)置了 CameraX Preview 用例。請(qǐng)注意,CameraX 中的用例代表了可以使用該庫(kù)實(shí)現(xiàn)的各種工作流程的配置,即預(yù)覽、捕獲、錄制和分析。視圖模型還將界面綁定到相機(jī)提供程序:
class CameraPreviewViewModel : ViewModel() {
// Used to set up a link between the Camera and your UI.
private val _surfaceRequest = MutableStateFlow(null)
val surfaceRequest: StateFlow = _surfaceRequest
private val cameraPreviewUseCase = Preview.Builder().build().apply {
setSurfaceProvider { newSurfaceRequest ->
_surfaceRequest.update { newSurfaceRequest }
}
}
suspend fun bindToCamera(appContext: Context, lifecycleOwner: LifecycleOwner) {
val processCameraProvider = ProcessCameraProvider.awaitInstance(appContext)
processCameraProvider.bindToLifecycle(
lifecycleOwner, DEFAULT_FRONT_CAMERA, cameraPreviewUseCase
)
// Cancellation signals we're done with the camera
try { awaitCancellation() } finally { processCameraProvider.unbindAll() }
}
}
此處會(huì)執(zhí)行大量操作。代碼定義了一個(gè) CameraPreviewViewModel 類,負(fù)責(zé)管理相機(jī)預(yù)覽。此類使用 CameraX Preview 構(gòu)建器來(lái)配置預(yù)覽與界面的綁定方式。bindToCamera 函數(shù)用于初始化相機(jī),并將其綁定到指定的 LifecycleOwner,以確保相機(jī)至少在生命周期處于 "啟動(dòng)" 狀態(tài)時(shí)運(yùn)行,并啟動(dòng)預(yù)覽流。
相機(jī)作為相機(jī)庫(kù)的內(nèi)部組件,需要渲染到界面提供的 surface。因此,庫(kù)需要有一種方法來(lái)請(qǐng)求 surface。這正是 SurfaceRequest 的用途。因此,每當(dāng)相機(jī)表示需要 surface 時(shí),就會(huì)觸發(fā) surfaceRequest。然后將該請(qǐng)求轉(zhuǎn)發(fā)給界面,以便將 surface 傳遞給請(qǐng)求對(duì)象。
最后,我們需要等待界面與相機(jī)完成綁定,并確保釋放相機(jī)資源以避免資源泄漏。
實(shí)現(xiàn)相機(jī)預(yù)覽界面
現(xiàn)在我們有了一個(gè)視圖模型,可以實(shí)現(xiàn) CameraPreviewContent 可組合項(xiàng)。該項(xiàng)從視圖模型中讀取 surface 請(qǐng)求,在可組合項(xiàng)位于組合樹(shù)中時(shí)綁定到相機(jī),并從庫(kù)中調(diào)用 CameraXViewfinder。
@Composable
fun CameraPreviewContent(
viewModel: CameraPreviewViewModel,
modifier: Modifier = Modifier,
lifecycleOwner: LifecycleOwner = LocalLifecycleOwner.current
) {
val surfaceRequest by viewModel.surfaceRequest.collectAsStateWithLifecycle()
val context = LocalContext.current
LaunchedEffect(lifecycleOwner) {
viewModel.bindToCamera(context.applicationContext, lifecycleOwner)
}
surfaceRequest?.let { request ->
CameraXViewfinder(
surfaceRequest = request,
modifier = modifier
)
}
}
如上部分所述,surfaceRequest 允許相機(jī)庫(kù)在需要渲染時(shí)請(qǐng)求一個(gè) surface。在這段代碼中,我們收集這些 surfaceRequest 實(shí)例,并將它們轉(zhuǎn)發(fā)給屬于 camera-compose 組件的 CameraXViewfinder。
結(jié)果
就這樣,我們構(gòu)建了一個(gè)功能齊全的全屏取景器。了解完整代碼片段,請(qǐng)?jiān)L問(wèn)相關(guān)文檔:
https://gist.github.com/JolandaVerhoef/74d4696b804736c698450bd34b5c9ff8
本文中的代碼段包含以下許可證:
// Copyright 2024 Google LLC. SPDX-License-Identifier: Apache-2.0
-
Google
+關(guān)注
關(guān)注
5文章
1801瀏覽量
60274 -
相機(jī)
+關(guān)注
關(guān)注
5文章
1538瀏覽量
55418 -
API
+關(guān)注
關(guān)注
2文章
2166瀏覽量
66253
原文標(biāo)題:在 Jetpack Compose 中解鎖 CameraX 的強(qiáng)大功能
文章出處:【微信號(hào):Google_Developers,微信公眾號(hào):谷歌開(kāi)發(fā)者】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
深入了解CameraX 1.1版本的視頻拍攝功能
詳解Jetpack Compose 1.1版本的新功能
Jetpack Compose基礎(chǔ)知識(shí)科普
Compose在社區(qū)中的反響
Jetpack Compose 更新一覽 | 2022 Android 開(kāi)發(fā)者峰會(huì)
Google計(jì)劃用Jetpack Compose來(lái)重建Android系統(tǒng)中的設(shè)置應(yīng)用
Compose for Wear OS 1.1 推出穩(wěn)定版: 了解新功能!
Kotlin聲明式UI框架Compose Multiplatform支持iOS
在 I/O 看未來(lái) | Jetpack 新功能一覽
Jetpack WindowManager 1.1 穩(wěn)定版
Jetpack Compose和設(shè)備類型的三大重要更新
NVIDIA JetPack 6.0版本的關(guān)鍵功能
解析NVIDIA JetPack 6.1的新功能
詳解Jetpack Compose布局流程

在Jetpack Compose中解鎖CameraX的強(qiáng)大功能
評(píng)論