kotlin 如何停止相机工作时,它不再可见的组成?

j0pj023g  于 2022-11-30  发布在  Kotlin
关注(0)|答案(2)|浏览(130)

我有一个bottomSheetScaffold,其中包含一个BottomSheet
BottomSheet使用设备的Camera,我使用CameraX和Google的MLkit进行条形扫描
让我们考虑许可被接受会发生什么(不正确):一旦我向上展开底部表单,我将显示CameraPreview、show camera preview和分析预览图像的ImageAnalyzer。
现在bottomSheet已展开,相机预览可见并按预期工作,然后我折叠bottomSheet,但相机仍在工作(分析器也是如此,imageAnalysis.clearAnalyzer()清除分析部分)
结果:这不是我本意
如何在bottomSheetState折叠后停止camera工作和使用资源,并仅在bottomSheetState展开时允许camera
工作原理(错误):

我遇到的问题是,摄像头绑定到活动的生命周期,而不是组合本身,当重新组合发生时,它仍然认为摄像头是活动的,因为它没有附加到组合生命周期
合成的工作原理:

编码:

@OptIn(ExperimentalMaterialApi::class)
@Composable
fun BottomSheetContent(
    modifier: Modifier = Modifier,
    bottomSheetState: BottomSheetState
) {
    Column(
        modifier = modifier
            .fillMaxWidth()
            .fillMaxHeight(0.8f)
    ) {
        PeekBar()
        ScanningSerialTextTitle(modifier)

        if (bottomSheetState.isExpanded) {
            CameraBox(modifier)
        } else {

            EmptyBox()
        }
    }
}

@Composable
fun EmptyBox(modifier: Modifier = Modifier) {
    Box(
        modifier = modifier
            .fillMaxSize()
            .background(color = Color.DarkGray)
    )
}

@OptIn(ExperimentalPermissionsApi::class)
@Composable
fun CameraBox(modifier: Modifier = Modifier) {
    val cameraPermissionState = rememberPermissionState(permission = Manifest.permission.CAMERA)


    val lifeCycleOwner = LocalLifecycleOwner.current
    DisposableEffect(key1 = lifeCycleOwner, effect = {
        val observer = LifecycleEventObserver { _, event ->
            if (event == Lifecycle.Event.ON_START) {
                cameraPermissionState.launchPermissionRequest()
            }
        }
        lifeCycleOwner.lifecycle.addObserver(observer)
        onDispose { lifeCycleOwner.lifecycle.removeObserver(observer) }
    })

    cameraPermissionState.handlePermissionCases(
        ShouldShowRationaleContent = {
            ShouldShowRationaleContent(cameraPermissionState = cameraPermissionState)
        },
        PermissionDeniedPermanentlyContent = {
            PermissionDeniedPermanentContent()
        }) {
        val context = LocalContext.current
        val barCodeVal = remember { mutableStateOf("") }
        CameraPreview(onBarcodeDetected = { barcodes ->
            barcodes.forEach { barcode ->
                barcode.rawValue?.let { barcodeValue ->
                    barCodeVal.value = barcodeValue
                    Toast.makeText(context, barcodeValue, Toast.LENGTH_SHORT).show()
                }
            }
        }, onBarcodeFailed = {}, onBarcodeNotFound = {})
    }

}

@Composable
fun CameraPreview(
    modifier: Modifier = Modifier,
    onBarcodeDetected: (barcodes: List<Barcode>) -> Unit,
    onBarcodeFailed: (exception: Exception) -> Unit,
    onBarcodeNotFound: (text: String) -> Unit,
) {
    val context = LocalContext.current
    val lifecycleOwner = LocalLifecycleOwner.current

    AndroidView(
        modifier = modifier.fillMaxSize(),
        factory = { androidViewContext -> initPreviewView(androidViewContext) },
        update = { previewView: PreviewView ->
            val cameraSelector: CameraSelector = buildCameraSelector(CameraSelector.LENS_FACING_BACK)
            val cameraExecutor: ExecutorService = Executors.newSingleThreadExecutor()
            val cameraProviderFuture: ListenableFuture<ProcessCameraProvider> =
                ProcessCameraProvider.getInstance(context)

            val preview = buildPreview().also {
                it.setSurfaceProvider(previewView.surfaceProvider)
            }

            val barcodeAnalyser = BarCodeAnalyser(
                onBarcodeDetected = onBarcodeDetected,
                onBarcodeFailed = onBarcodeFailed,
                onBarCodeNotFound = onBarcodeNotFound
            )
            val imageAnalysis: ImageAnalysis =
                buildImageAnalysis(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST).also {
                    it.setAnalyzer(cameraExecutor, barcodeAnalyser)
                }

            cameraProviderFuture.addListener({
                val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()
                try {
                    cameraProvider.unbindAll() //Make sure we only use 1 usecase related to camera
                    val camera = cameraProvider.bindToLifecycle(
                        lifecycleOwner,
                        cameraSelector,
                        preview,
                        imageAnalysis
                    )
                    camera.cameraControl.enableTorch(true)

                } catch (e: Exception) {
                    Log.d("TAG", "CameraPreview: ${e.localizedMessage}")
                }
            }, ContextCompat.getMainExecutor(context))
        }
    )
}

private fun initPreviewView(androidViewContext: Context): PreviewView {
     val previewView = PreviewView(androidViewContext).apply {
        implementationMode = PreviewView.ImplementationMode.COMPATIBLE
    }
    return previewView
}

private fun buildPreview(): Preview {
    return Preview.Builder().build()
}

private fun buildImageAnalysis(imageAnalysisStrategy: Int): ImageAnalysis {
    return ImageAnalysis.Builder()
        .setBackpressureStrategy(imageAnalysisStrategy)
        .build()
}

private fun buildCameraSelector(cameraLens: Int): CameraSelector {
    return CameraSelector.Builder()
        .requireLensFacing(cameraLens)
        .build()
}

我尝试过:我尝试将BottomSheetState的状态传递给可组合对象,并检查状态,这应该会触发重新组合,但由于我使用Android的Camera作为View,因此这并不能解决问题

b1uwtaje

b1uwtaje1#

首先您将为摄像机状态创建一个变量

var showCamera by remember {mutableStateOf(false)}

**然后,**您将检测到上下拖动底部纸张的时间

val modalBottomSheetState = rememberModalBottomSheetState(initialValue = ModalBottomSheetValue.Hidden)
val scope = rememberCoroutineScope()
LaunchedEffect(modalBottomSheetState.currentValue) {
    println(modalBottomSheetState.currentValue)
}
if (modalBottomSheetState.currentValue != ModalBottomSheetValue.Hidden) {
    DisposableEffect(Unit) {
        onDispose {

//现在底层工作表被隐藏showcamera=false

}
    }
}

最后您将检查showcamrea = true是否为真,然后请求此摄像头或类似视图

if(showcamera){
AndroidView(
        modifier = modifier.fillMaxSize(),
        factory = { androidViewContext -> initPreviewView(androidViewContext) },
        update = { previewView: PreviewView ->........
last of android view camera
}

希望对你有帮助!

kuarbcqp

kuarbcqp2#

我找到了一个解决方案,当composable从composition中删除时,我使用DisposableEffect关闭摄像头
首先,在代码中的CameraPreview组合函数上,定义一个ProcessCameraProvider类型的变量,并将其赋值为空值

var cameraProvider: ProcessCameraProvider? = null

然后,您将定义一个DisposableEffect,其密钥为cameraProvider,当组合解组合时,您将关闭摄像头

DisposableEffect(key1 = cameraProvider) {
    onDispose {
        cameraProvider?.let { it.unbindAll() } // closes the camera
    }
}

替换旧代码行

val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()

全新的cameraProvider

cameraProvider = cameraProviderFuture.get()

然后在try-catch块中,由于我们使用的是空值,当需要检查它是否为空时,我们将使用let

try {
    cameraProvider?.let {
        it.unbindAll() //Make sure we only use 1 usecase related to camera

        val camera = it.bindToLifecycle(
            lifecycleOwner, cameraSelector, preview, imageAnalysis
        )

        camera.cameraControl.enableTorch(true) // TODO: Debug mode only
    }

} catch (e: Exception) {
    Log.d("TAG", "CameraPreview: ${e.localizedMessage}")
}
    • 完整代码:**
@Composable
fun CameraPreview( 
    modifier: Modifier = Modifier,
    onBarcodeDetected: (barcodes: List<Barcode>) -> Unit,
    onBarcodeFailed: (exception: Exception) -> Unit,
    onBarcodeNotFound: (text: String) -> Unit,
) {
    val context = LocalContext.current
    val lifecycleOwner = LocalLifecycleOwner.current

    var cameraProvider: ProcessCameraProvider? = null
    DisposableEffect(key1 = cameraProvider) {
        onDispose {
            cameraProvider?.let { it.unbindAll() }
        }
    }

    AndroidView(
        modifier = modifier.fillMaxSize(),
        factory = { androidViewContext -> initPreviewView(androidViewContext) },
        update = { previewView: PreviewView ->
            val cameraSelector: CameraSelector =
                buildCameraSelector(CameraSelector.LENS_FACING_BACK)
            val cameraExecutor: ExecutorService = Executors.newSingleThreadExecutor()
            val cameraProviderFuture: ListenableFuture<ProcessCameraProvider> =
                ProcessCameraProvider.getInstance(context)

            cameraProviderFuture.addListener({
                cameraProvider = cameraProviderFuture.get()

                val preview = buildPreview().also {
                    it.setSurfaceProvider(previewView.surfaceProvider)
                }

                val barcodeAnalyser = BarCodeAnalyser(
                    onBarcodeDetected = onBarcodeDetected,
                    onBarcodeFailed = onBarcodeFailed,
                    onBarCodeNotFound = onBarcodeNotFound
                )
                val imageAnalysis: ImageAnalysis =
                    buildImageAnalysis(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST).also {
                        it.setAnalyzer(cameraExecutor, barcodeAnalyser)
                    }


                try {
                    cameraProvider?.let {
                        it.unbindAll() //Make sure we only use 1 usecase related to camera

                        val camera = it.bindToLifecycle(
                            lifecycleOwner, cameraSelector, preview, imageAnalysis
                        )

                        camera.cameraControl.enableTorch(true) // TODO: Debug mode only
                    } 

                } catch (e: Exception) {
                    Log.d("TAG", "CameraPreview: ${e.localizedMessage}")
                }
            }, ContextCompat.getMainExecutor(context))
        }
    )

}

相关问题