CameraX不能使用jetpack compose中的导航库



我在可组合屏幕上面临导航组合(2.4.0-alpha09)和相机库(1.1.0-alpha08)的问题。如果我只使用相机视图(在AndroidView中包装),一切工作正常,图像捕获工作。但是如果我在composable中使用navController来导航,图像捕获不工作,应用程序崩溃。

@ExperimentalCoilApi
@Composable
fun CameraScreen(navController: NavController) {
val context = LocalContext.current
val lifecycleOwner = LocalLifecycleOwner.current
Scaffold {
CameraPreviewScreen(
modifier = Modifier.fillMaxSize(),
//                navController = navController,
context = context,
lifecycleOwner = lifecycleOwner,
outPutDirectory = getFilesDirectory(context = context),
onMediaCaptured = { uri ->
//                    navController.navigate(
//                        Screens.AddPostDetails.withArgs(
//                            URLEncoder.encode(
//                                uri.toString(),
//                                StandardCharsets.UTF_8.toString()
//                            )
//                        )
//                    )
}
)
}
}
@ExperimentalCoilApi
@Composable
fun CameraPreviewScreen(
modifier: Modifier = Modifier,
//    navController: NavController,
context: Context,
lifecycleOwner: LifecycleOwner,
outPutDirectory: File,
onMediaCaptured: (Uri?) -> Unit
) {
val cameraProviderFuture = remember { ProcessCameraProvider.getInstance(context) }
val cameraProvider = cameraProviderFuture.get()
val executor = ContextCompat.getMainExecutor(context)
var imageCapture: ImageCapture? = null
var cameraSelector: CameraSelector?
var camera: Camera? = null
val previewView = PreviewView(context)
val preview = Preview.Builder().build().also {
it.setSurfaceProvider(previewView.surfaceProvider)
}
var lensFacing by remember { mutableStateOf(CameraSelector.LENS_FACING_BACK) }
var flashEnabled by remember { mutableStateOf(false) }
var flashRes by remember { mutableStateOf(R.drawable.ic_outlined_flash_on) }
Box {
AndroidView(
factory = { ctx ->
cameraProviderFuture.addListener(
{
cameraSelector = CameraSelector.Builder()
.requireLensFacing(lensFacing)
.build()
imageCapture = ImageCapture.Builder()
.setTargetRotation(previewView.display.rotation)
.build()
cameraProvider.unbindAll()
camera = cameraProvider.bindToLifecycle(
lifecycleOwner,
cameraSelector as CameraSelector,
imageCapture,
preview
)
}, executor
)
previewView
},
modifier = modifier
)
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
.align(Alignment.TopStart)
) {
IconButton(onClick = {
Log.d(TAG, "back clicked")
//                navController.popBackStack()
}) {
Icon(
modifier = Modifier.iconSize(),
imageVector = Icons.Filled.ArrowBack,
contentDescription = null,
tint = MaterialTheme.colors.surface
)
}
}
Row(
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
.clip(RoundedCornerShape(16.dp))
.background(background, RoundedCornerShape(16.dp))
.padding(8.dp)
.align(Alignment.BottomCenter)
) {
IconButton(
onClick = {
camera?.let {
if (it.cameraInfo.hasFlashUnit()) {
flashEnabled = !flashEnabled
flashRes =
if (flashEnabled) R.drawable.ic_outlined_flash_off else R.drawable.ic_outlined_flash_on
it.cameraControl.enableTorch(flashEnabled)
}
}
}) {
Icon(
modifier = Modifier.size(34.dp),
painter = painterResource(id = flashRes),
contentDescription = null,
tint = MaterialTheme.colors.surface
)
}
Button(
modifier = Modifier
.size(70.dp)
.background(captureIconColor, CircleShape)
.shadow(4.dp, CircleShape)
.clip(CircleShape)
.border(5.dp, Color.LightGray, CircleShape),
colors = ButtonDefaults.buttonColors(backgroundColor = captureIconColor),
onClick = {
val imgCapture = imageCapture ?: return@Button
val photoFile = File(
outPutDirectory,
SimpleDateFormat("yyyyMMdd-HHmmss-SSS", Locale.US)
.format(System.currentTimeMillis()) + ".jpg"
)
val outputOptions = ImageCapture.OutputFileOptions.Builder(photoFile).build()
imgCapture.takePicture(
outputOptions,
executor,
object : ImageCapture.OnImageSavedCallback {
override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
Log.d(TAG, "image saved: ${photoFile.path}")
onMediaCaptured(Uri.fromFile(photoFile))
}
override fun onError(exception: ImageCaptureException) {
Toast.makeText(
context, "Something went wrong", Toast.LENGTH_SHORT
).show()
}
}
)
}
) {
}
IconButton(
onClick = {
lensFacing =
if (lensFacing == CameraSelector.LENS_FACING_BACK) CameraSelector.LENS_FACING_FRONT else CameraSelector.LENS_FACING_BACK
cameraSelector = CameraSelector.Builder()
.requireLensFacing(lensFacing)
.build()
cameraProvider.unbindAll()
cameraProvider.bindToLifecycle(
lifecycleOwner,
cameraSelector as CameraSelector,
imageCapture,
preview
)
}) {
Icon(
modifier = Modifier.size(34.dp),
painter = painterResource(id = R.drawable.ic_outlined_rotate),
contentDescription = null,
tint = MaterialTheme.colors.surface
)
}
}
}
}

确保你有拍照权限。你可以在Settings ->应用程序→你的应用->权限→打开拍摄权限

如果您在此列表中没有看到camera,这意味着您没有在AndroidMainfest.xml中添加所需的权限,请查看此代码库。

要求用户从你的应用程序启用权限,使用伴奏权限。


现在进入代码。所有可组合的函数都是视图构建器,这意味着它们可以在应用生命周期中被多次调用。您正在创建许多没有remember的变量,这意味着它们将在第一次重组时失去它的状态。

AndroidViewfactory在视图创建时被调用,并且你应该只在该块中创建android视图。

在你的代码中,你是在外部创建它的,这意味着在第一次重组之后,preview将有一个无效的surfaceProvider

我建议你从撰写状态文档开始,包括这个youtube视频,它解释了基本原则。

你的代码应该像这样更新:

val cameraProviderFuture = remember(context) { ProcessCameraProvider.getInstance(context) }
val cameraProvider = remember(cameraProviderFuture) { cameraProviderFuture.get() }
val executor = remember(context) { ContextCompat.getMainExecutor(context) }
var imageCapture: ImageCapture? by remember { mutableStateOf(null) }
var cameraSelector: CameraSelector? by remember { mutableStateOf(null) }
var camera: Camera? by remember { mutableStateOf(null) }
var preview by remember { mutableStateOf<Preview?>(null) }
var lensFacing by remember { mutableStateOf(CameraSelector.LENS_FACING_BACK) }
var flashEnabled by remember { mutableStateOf(false) }
var flashRes by remember { mutableStateOf(R.drawable.ic_redo) }
Box {
AndroidView(
factory = { ctx ->
val previewView = PreviewView(ctx)
cameraProviderFuture.addListener(
{
cameraSelector = CameraSelector.Builder()
.requireLensFacing(lensFacing)
.build()
imageCapture = ImageCapture.Builder()
.setTargetRotation(previewView.display.rotation)
.build()
cameraProvider.unbindAll()
camera = cameraProvider.bindToLifecycle(
lifecycleOwner,
cameraSelector as CameraSelector,
imageCapture,
preview
)
}, executor
)
preview = Preview.Builder().build().also {
it.setSurfaceProvider(previewView.surfaceProvider)
}
previewView
},
modifier = modifier
)
...
}

最新更新