扫描预览
@Composable
fun ScannerContent(modifier: Modifier, onScan: (String) -> Unit) {
val lifecycleOwner = LocalLifecycleOwner.current
val context = LocalContext.current
var cameraControl: CameraControl? by remember { mutableStateOf(null) }
AndroidView(factory = { ctx ->
PreviewView(ctx).apply {
scaleType = PreviewView.ScaleType.FILL_CENTER
}
}, modifier = modifier, update = { previewView ->
val cameraExecutor = Executors.newSingleThreadExecutor()
val cameraProviderFuture = ProcessCameraProvider.getInstance(context)
cameraProviderFuture.addListener({
val cameraProvider = cameraProviderFuture.get()
val preview = Preview.Builder().build().also {
it.setSurfaceProvider(previewView.surfaceProvider)
}
// 2. 创建 ImageAnalysis 用例(核心分析逻辑)
val imageAnalysis = ImageAnalysis.Builder().setImageQueueDepth(1)
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST) // 仅保留最新帧
.setOutputImageFormat(ImageAnalysis.OUTPUT_IMAGE_FORMAT_YUV_420_888) // 输出格式
.build().apply {
setAnalyzer(cameraExecutor, BarcodeAnalyzer(onScan))
}
val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
try {
cameraProvider.unbindAll()
val camera = cameraProvider.bindToLifecycle(
lifecycleOwner, cameraSelector, preview, imageAnalysis
)
cameraControl = camera.cameraControl
} catch (e: Exception) {
Log.e("QRScanner", "Camera Bind Error", e)
}
}, ContextCompat.getMainExecutor(context))
})
}
图像分析采用
"com.google.mlkit:barcode-scanning"
class BarcodeAnalyzer(private val onScan: (String) -> Unit) : ImageAnalysis.Analyzer {
val options = BarcodeScannerOptions.Builder()
.setBarcodeFormats(Barcode.FORMAT_QR_CODE) // 仅扫描标准二维码
.build()
private val scanner = BarcodeScanning.getClient()
@OptIn(ExperimentalGetImage::class)
override fun analyze(imageProxy: ImageProxy) {
val mediaImage = imageProxy.image ?: run {
imageProxy.close()
return
}
if (mediaImage.format != ImageFormat.YUV_420_888) {
imageProxy.close()
return
}
val image = InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees)
scanner.process(image)
.addOnSuccessListener { barcodes ->
barcodes.firstOrNull()?.rawValue?.let { result ->
onScan(result)
}
}
.addOnCompleteListener {
imageProxy.close()
}
}
}