在 Jetpack compose 中使用 Zxing 库

Using Zxing Library with Jetpack compose

我正在尝试使用 zxing 库实现二维码扫描仪。为此,我在屏幕上添加了一个按钮,点击它,我将启动扫描仪,如下所示

Button(
        onClick = {
            val intentIntegrator = IntentIntegrator(context)
            intentIntegrator.setPrompt(QrScanLabel)
            intentIntegrator.setOrientationLocked(true)
            intentIntegrator.initiateScan()
        },
        modifier = Modifier
            .fillMaxWidth()
    ) {
        Text(
            text = QrScanLabel
        )
    }

但是,它启动了一个意图,它期望 onActivityResult 方法返回结果。 Jetpack compose 使用 rememberLauncherForActivityResult 如下所示

val intentLauncher = rememberLauncherForActivityResult(
        contract = ActivityResultContracts.StartIntentSenderForResult()
    ) {
        if (it.resultCode != RESULT_OK) {
            return@rememberLauncherForActivityResult
        }
        ...
    }

但是我们如何在这里将两者整合在一起?

我用同一个库做了一个临时解决方案: Gradle 依赖关系:

implementation('com.journeyapps:zxing-android-embedded:4.1.0') { transitive = false }
implementation 'com.google.zxing:core:3.4.0'

我的新屏幕带有 Jetpack Compose 和摄像头捕捉功能,适用于我的应用程序:

@Composable
fun AdminClubMembershipScanScreen(navController: NavHostController) {
    val context = LocalContext.current
    var scanFlag by remember {
        mutableStateOf(false)
    }

    val compoundBarcodeView = remember {
        CompoundBarcodeView(context).apply {
            val capture = CaptureManager(context as Activity, this)
            capture.initializeFromIntent(context.intent, null)
            this.setStatusText("")
            capture.decode()
            this.decodeContinuous { result ->
                if(scanFlag){
                    return@decodeContinuous
                }
                scanFlag = true
                result.text?.let { barCodeOrQr->
                    //Do something and when you finish this something
                    //put scanFlag = false to scan another item
                    scanFlag = false
                }
                //If you don't put this scanFlag = false, it will never work again.
                //you can put a delay over 2 seconds and then scanFlag = false to prevent multiple scanning 
                
            }
        }
    }

    AndroidView(
        modifier = Modifier,
        factory = { compoundBarcodeView },
    )
}

我有一个问题,我和你有同样的代码,但我不知道为什么它向我显示黑屏

代码添加产品

@ExperimentalPermissionsApi
@Composable
fun AddProduct(
    navController: NavController
) {
    val context = LocalContext.current
    var scanFlag by remember {
        mutableStateOf(false)
    }

    val compoundBarcodeView = remember {
        CompoundBarcodeView(context).apply {
            val capture = CaptureManager(context as Activity, this)
            capture.initializeFromIntent(context.intent, null)
            this.setStatusText("")
            capture.decode()
            this.decodeContinuous { result ->
                if(scanFlag){
                    return@decodeContinuous
                }
                scanFlag = true
                result.text?.let { barCodeOrQr->
                    //Do something

                }
                scanFlag = false
            }
        }
    }

    AndroidView(
        modifier = Modifier.fillMaxSize(),
        factory = { compoundBarcodeView },
    )

}

已接受答案的附录

这个答案深入探讨了@Bharat Kumar 和@Jose Pose S 评论的问题 在接受的答案中。

我基本上只是在我的代码中实现了接受的答案,然后在定义 compundBarCodeView

之后添加了以下代码
DisposableEffect(key1 = "someKey" ){
    compoundBarcodeView.resume()
    onDispose {
        compoundBarcodeView.pause()
    }
}

这可确保扫描仪仅在前台处于活动状态并减轻我们设备的负担时才处于活动状态。

TL;DR

即使在您成功扫描 QR 码并离开扫描仪屏幕后,条形码视图仍会“困扰”您,继续从后台扫描。你通常不想要的。即使您使用布尔标志来防止扫描仪在焦点离开扫描仪后做任何事情,它仍然会给您的处理器带来负担并减慢您的 UI 因为仍然有一个过程不断解密 hi-res 背景图片。

因为zxing-android-embedded:4.3.0有一个ScanContract,可以直接从Compose中使用:

val scanLauncher = rememberLauncherForActivityResult(
    contract = ScanContract(),
    onResult = { result -> Log.i(TAG, "scanned code: ${result.contents}") }
)

Button(onClick = { scanLauncher.launch(ScanOptions()) }) {
    Text(text = "Scan barcode")
}