使用CameraX+ML-Kit实现文字识别时首次识别后相机黑屏怎么解决?
问题根因
你的黑屏问题来自三处核心错误:
- 每次识别到文字后都会重新加载布局、替换页面根View,新的
PreviewView没有绑定相机预览流,自然没有画面 - 图片识别成功后没有关闭
ImageProxy,导致相机输出队列被占满,无法继续输出预览帧 - 布局中
FrameLayout下的约束布局属性不生效,会导致文字位置不符合预期
修复步骤
1. 调整Activity代码逻辑
将ViewBinding改为全局成员,仅在onCreate中初始化一次,删除其余位置的inflate和setContentView调用,同时补全ImageProxy的释放逻辑:
class MainActivity : AppCompatActivity() { // 全局ViewBinding private lateinit var binding: ActivityMainBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // 仅在这里初始化一次 binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) if (allPermissionsGranted()) { startCamera() } else { requestPermissions() } } private val cameraExecutor: ExecutorService by lazy { Executors.newSingleThreadExecutor() } override fun onDestroy() { super.onDestroy() cameraExecutor.shutdown() } private companion object { val TAG: String = MainActivity::class.java.simpleName const val REQUEST_CODE_PERMISSIONS = 10 val REQUIRED_PERMISSIONS = arrayOf(Manifest.permission.CAMERA) } private fun allPermissionsGranted() = REQUIRED_PERMISSIONS.all { ContextCompat.checkSelfPermission(baseContext, it) == PackageManager.PERMISSION_GRANTED } private fun requestPermissions() { ActivityCompat.requestPermissions( this, REQUIRED_PERMISSIONS, REQUEST_CODE_PERMISSIONS ) } override fun onRequestPermissionsResult( requestCode: Int, permissions: Array<String>, grantResults: IntArray ) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) if (requestCode == REQUEST_CODE_PERMISSIONS) { if (allPermissionsGranted()) { startCamera() } else { Toast.makeText( this, "Permissions not granted.", Toast.LENGTH_SHORT ).show() finish() } } } class TextReaderAnalyzer( private val textFoundListener: (String) -> Unit ) : ImageAnalysis.Analyzer { @SuppressLint("UnsafeOptInUsageError") override fun analyze(imageProxy: ImageProxy) { imageProxy.image?.let {process(it, imageProxy) } } private fun process(image: Image, imageProxy: ImageProxy) { try { readTextFromImage(InputImage.fromMediaImage(image, 90), imageProxy) } catch (e: IOException) { Log.d(TAG, "Failed to load the image") e.printStackTrace() imageProxy.close() // 异常处释放资源 } } @SuppressLint("UnsafeOptInUsageError") private fun readTextFromImage(image: InputImage, imageProxy: ImageProxy) { TextRecognition.getClient() .process(image) .addOnSuccessListener { visionText -> processTextFromImage(visionText) imageProxy.close() // 识别成功必须释放资源 } .addOnFailureListener { error -> Log.d(TAG, "Failed to process the image") error.printStackTrace() imageProxy.close() } } private fun processTextFromImage(visionText: Text) { for (block in visionText.textBlocks) { for (line in block.lines) { for (element in line.elements) { textFoundListener(element.text) } } } } } private val imageAnalyzer by lazy { ImageAnalysis.Builder() .setTargetAspectRatio(AspectRatio.RATIO_16_9) .build() .also { it.setAnalyzer( cameraExecutor, TextReaderAnalyzer(::onTextFound) ) } } private fun onTextFound(foundText: String) { // 直接用全局binding更新文字即可,不要重新加载布局 binding.Result.text = foundText } private fun startCamera() { // 删掉这里的inflate和setContentView val cameraProviderFuture = ProcessCameraProvider.getInstance(this) cameraProviderFuture.addListener( { val preview = Preview.Builder() .build() .also { it.setSurfaceProvider(binding.cameraPreviewView.surfaceProvider) } cameraProviderFuture.get().bind(preview, imageAnalyzer) }, ContextCompat.getMainExecutor(this) ) } private fun ProcessCameraProvider.bind( preview: Preview, imageAnalyzer: ImageAnalysis ) = try { unbindAll() bindToLifecycle( this@MainActivity, CameraSelector.DEFAULT_BACK_CAMERA, preview, imageAnalyzer ) } catch (ise: IllegalStateException) { Log.e(TAG, "Binding failed", ise) } }
2. 调整布局属性
FrameLayout不支持约束布局属性,直接用layout_gravity控制文字位置即可:
<FrameLayout android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <androidx.camera.view.PreviewView android:id="@+id/cameraPreviewView" android:layout_width="match_parent" android:layout_height="match_parent"/> <TextView android:id="@+id/Result" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="top" android:paddingTop="16dp" android:fontFamily="sans-serif-black" android:textColor="@color/white" android:textSize="20sp" android:textStyle="bold" android:gravity="center_horizontal"/> </FrameLayout>
修改后识别到文字时只会更新TextView的内容,不会打断相机预览流,画面就不会黑屏了。
内容的提问来源于stack exchange,提问作者UncannyFruit
相关产品推荐
相关产品推荐

