Android CameraX ничего не показывает

Я реализовал новый образец, вот ссылка который описывает новый API CameraX из Google codelabs, но 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, 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
    }
}

person Max    schedule 09.05.2019    source источник
comment
эй, я получаю изображения, повернутые вправо, у тебя была эта проблема ??   -  person Pemba Tamang    schedule 26.09.2019
comment
Это ошибка библиотеки из-за неверных метаданных !, но только для передней линзы! Надеюсь, это будет исправлено с помощью бета-версии!   -  person Himanshu Walia    schedule 29.10.2019
comment
Эта проблема исправлена? Я столкнулся с той же проблемой. когда я привязываю жизненный цикл к preview, он работает нормально, но при привязке к imageCapture это черный экран   -  person Maryam    schedule 25.07.2020


Ответы (4)


Чтобы прикрепить SurfaceTexture, необходимо удалить и снова добавить TextureView из родительского представления. Это связано с тем, что TextureView внутренне создает свой собственный SurfaceTexture после того, как он присоединяется к иерархии представлений, и этот внутренний SurfaceTexture правильно отсоединяется только после того, как родительский TextureView удаляется из иерархии представлений. Вы должны изменить preview.setOnPreviewOutputUpdateListener на:

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

Похоже, вы скопировали код из codelab, который теперь был обновлен и теперь включает повторное прикрепление представления. официальный образец также реализует повторное прикрепление этого представления.

person Oscar Wahltinez    schedule 14.05.2019
comment
Это должен быть принятый ответ. Это решило проблему для меня. - person Mauro Banze; 21.06.2019
comment
Я согласен с @Mauro Banze - person Lena Bru; 10.11.2019
comment
это определенно решило проблему для меня. Хотя это больше похоже на Vodoo-bad-design, чем на четко определенный API. - person user1222936; 25.11.2019
comment
setOnPreviewOutputUpdateListener больше не существует !! - person Fattie; 11.03.2021

Котлин код Оскар Вальтинес на Java:

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

Я столкнулся с той же проблемой, когда следил за codeLabs. Я заблокировал экран, затем снова включил его, и вдруг он заработал нормально, функция захвата тоже сработала. Я понятия не имею об этой ситуации, но вы можете попробовать этот способ в качестве обходного пути. Я использую Q beta 3 в Pixel 3.

PS: вы можете просто запустить событие onStop и onStart для Activity (например, нажмите кнопку домой и снова откройте приложение), предварительный просмотр в реальном времени будет работать. На мой взгляд, эта проблема связана с CameraX.bindToLifecycle.

person Tuan Nguyen    schedule 10.05.2019
comment
Ваше решение работает хорошо! Определенно проблема внутри метода bindToLifecycle (), когда я закомментировал этот метод, у меня не было никаких исключений в Logcat, но предварительный просмотр тоже не работал - person Max; 10.05.2019
comment
Поскольку bindToLifeCycle() является частью потока CameraX, чтобы определить, когда предварительный просмотр должен начинаться или останавливаться, на основе жизненного цикла активности / фрагмента, поэтому он нам абсолютно необходим. Вы можете сослаться на документ cameraX или посмотреть сеанс CameraX в Google IO 2019: youtu.be/kuv8uK-5CLY получить дополнительную информацию. Нам нечего делать, кроме как ждать, пока Google исправит эту проблему :). - person Tuan Nguyen; 11.05.2019

Этот код работает для меня

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