安卓CameraX无法显示画面

9

我实现了一个新的示例,这里是链接,介绍了来自Google codelabs的新CameraX API。但TextureView没有显示任何内容,并抛出以下异常:

OpenGLRenderer: [SurfaceTexture-0-7609-1] dequeueImage: SurfaceTexture没有连接到视图

其他相机示例,如Camera2和原生相机应用程序都可以正常工作。我使用了API级别为Q beta 3的模拟器。

class CameraXFragment : Fragment(), TextureView.SurfaceTextureListener {

    companion object {
        fun newInstance(): Fragment = CameraXFragment()
    }

    private val REQUEST_CODE_PERMISSIONS = 10
    private val REQUIRED_PERMISSIONS = arrayOf(Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE)

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? = inflater.inflate(R.layout.fragment_camera, container, false)

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        viewFinder.surfaceTextureListener = this
    }

    private fun startCamera() {
        CameraX.unbindAll()

        val previewConfig = PreviewConfig.Builder().apply {
            setTargetAspectRatio(Rational(1, 1))
            setTargetResolution(Size(320, 320))
        }.build()

        val preview = Preview(previewConfig)
        preview.setOnPreviewOutputUpdateListener {
            viewFinder.surfaceTexture = it.surfaceTexture
            updateTransform()
        }

        val imageCaptureConfig = ImageCaptureConfig.Builder()
                .apply {
                    setTargetAspectRatio(Rational(1, 1))
                    setCaptureMode(ImageCapture.CaptureMode.MIN_LATENCY)
                }.build()

        val imageCapture = ImageCapture(imageCaptureConfig)
        captureButton.setOnClickListener {
            val file = File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM), "${System.currentTimeMillis()}.jpg")
            imageCapture.takePicture(file,
                    object : ImageCapture.OnImageSavedListener {
                        override fun onError(error: ImageCapture.UseCaseError, message: String, t: Throwable?) {
                            t?.printStackTrace()
                        }

                        override fun onImageSaved(file: File) {
                            val msg = "Photo capture succeeded: ${file.absolutePath}"
                            Toast.makeText(requireContext(), msg, Toast.LENGTH_SHORT).show()
                        }
                    })
        }

        CameraX.bindToLifecycle(this, preview, imageCapture)
    }

    private fun updateTransform() {
        val matrix = Matrix()
        val centerX = viewFinder.width / 2f
        val centerY = viewFinder.height / 2f
        val rotationDegrees = when (viewFinder.display.rotation) {
            Surface.ROTATION_0 -> 0
            Surface.ROTATION_90 -> 90
            Surface.ROTATION_180 -> 180
            Surface.ROTATION_270 -> 270
            else -> return
        }
        matrix.postRotate(-rotationDegrees.toFloat(), centerX, centerY)
        viewFinder.setTransform(matrix)
    }

    override fun onSurfaceTextureSizeChanged(surface: SurfaceTexture, width: Int, height: Int) {
    }

    override fun onSurfaceTextureUpdated(surface: SurfaceTexture) {
    }

    override fun onSurfaceTextureDestroyed(surface: SurfaceTexture): Boolean {
        return true
    }

    override fun onSurfaceTextureAvailable(surface: SurfaceTexture?, width: Int, height: Int) {
        if (allPermissionsGranted()) {
            viewFinder.post { startCamera() }
        } else {
            requestPermissions(REQUIRED_PERMISSIONS, REQUEST_CODE_PERMISSIONS)
        }
        viewFinder.addOnLayoutChangeListener { _, _, _, _, _, _, _, _, _ ->
            updateTransform()
        }
    }

    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
        if (requestCode == REQUEST_CODE_PERMISSIONS) {
            if (allPermissionsGranted()) {
                viewFinder.post { startCamera() }
            } else {
                Toast.makeText(requireContext(), "Permissions are not granted", Toast.LENGTH_SHORT).show()
            }
        }
    }

    private fun allPermissionsGranted(): Boolean {
        for (permission in REQUIRED_PERMISSIONS) {
            if (ContextCompat.checkSelfPermission(requireContext(), permission) != PackageManager.PERMISSION_GRANTED) {
                return false
            }
        }
        return true
    }
}

嘿,我遇到了图片向右旋转的问题,你有这个问题吗? - Pemba Tamang
那是由于无效元数据导致库中的错误!只是针对前置镜头!希望这能在beta版中得到修复! - Himanshu Walia
这个问题解决了吗?我也遇到了同样的问题。当我将生命周期绑定到“预览”时,它可以正常工作,但是当绑定到“imageCapture”时,屏幕是黑色的。 - Maryam Mirzaie
5个回答

19

为了绑定SurfaceTexture,需要将TextureView从父视图中移除并重新添加。这是因为一旦TextureView附加到视图层次结构中,它会在内部创建自己的SurfaceTexture,只有在将父TextureView从视图层次结构中移除时才能正确地分离这个内部SurfaceTexture。您应该将 preview.setOnPreviewOutputUpdateListener 更改为:

preview.setOnPreviewOutputUpdateListener {
    val parent = viewFinder.parent as ViewGroup
    parent.removeView(viewFinder)
    viewFinder.surfaceTexture = it.surfaceTexture
    parent.addView(viewFinder, 0)
    updateTransform()
}

看起来您可能已经从codelab中复制了代码,该代码现在已更新以包括视图重新附加。 官方示例 也实现了此视图重新附加。


这应该是被接受的答案。它为我解决了问题。 - Mauro Banze
我同意@Mauro Banze的看法。 - Lena Bru
这对我来说绝对解决了问题。尽管它看起来更像是巫术式的糟糕设计,而不是良好定义的API。 - user1222936
1
setOnPreviewOutputUpdateListener已经不存在了!! - Fattie

5
我在跟随 CodeLabs 学习时遇到了同样的问题。我锁定了屏幕,然后再次打开屏幕,突然它就正常工作了,捕获功能也可以正常使用。 我对这种情况毫无头绪,但你可以尝试这种方法作为解决办法。我在 Pixel 3 上使用 Q beta 3。
PS:你可以触发 Activity 的 onStop 和 onStart 事件(例如:按 home 键并再次打开应用程序),直播预览将会正常工作。我认为,这个问题与 CameraX.bindToLifecycle 有关。

你的解决方案很好!肯定是在bindToLifecycle()方法内部出了问题,当我注释掉这个方法后,在Logcat中没有任何异常,但预览也无法工作。 - Max
1
因为bindToLifeCycle()是CameraX流程的一部分,用于根据活动/片段生命周期检测预览何时应该开始或停止,所以我们绝对需要它。您可以参考CameraX文档或观看Google IO 2019年的CameraX会话:https://youtu.be/kuv8uK-5CLY 获取更多信息。除了等待Google解决这个问题外,我们无事可做 :)。 - Tuan Nguyen

4

Kotlin 代码Oscar Wahltinez的Java版本:

ViewGroup parent = (ViewGroup) textureView.getParent();
parent.removeView(textureView);
parent.addView(textureView, 0);
SurfaceTexture surfaceTexture = previewOutput.getSurfaceTexture();
textureView.setSurfaceTexture(surfaceTexture);

1
除了this的答案之外。我通过从AndroidManifest.xml文件中删除应用级别的hardwareAccelerated="false"行来解决我的问题。

0

这段代码对我来说是有效的

  val parent = viewFinder.parent as ViewGroup
  parent.removeView(viewFinder)
  parent.addView(viewFinder, 0)
  val surfaceTexture: SurfaceTexture = it.surfaceTexture
  viewFinder.setSurfaceTexture(surfaceTexture)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接