为什么View.display返回null?

3

我正在尝试使用这个教程实现CameraX:https://codelabs.developers.google.com/codelabs/camerax-getting-started/#5 我的应用程序有一个活动、导航主机和两个片段。而且我在我的片段上使用了数据绑定。当我尝试获取显示旋转时,会出现错误。

这是我的布局文件:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <data>
        <variable
            name="viewModel"
            type="com.ayonix.faceticket.camerapreview.CameraPreviewViewModel" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".camerapreview.CameraPreviewFragment">

        <TextureView
            android:id="@+id/camera_preview"
            android:layout_width="0dp"
            android:layout_height="0dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintDimensionRatio="9:16"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

以下是我的代码片段:

class CameraPreviewFragment : Fragment() {

    private lateinit var binding: CameraPreviewFragmentBinding
    private lateinit var viewModel: CameraPreviewViewModel

    private val previewConfig = PreviewConfig.Builder().build()
    private val preview = Preview(previewConfig)

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        binding = CameraPreviewFragmentBinding.inflate(inflater, container, false)
        binding.lifecycleOwner = this

        val viewModelFactory = CameraPreviewViewModelFactory((activity as MainActivity).faceIDUtils)
        viewModel = ViewModelProviders.of(this, viewModelFactory).get(CameraPreviewViewModel::class.java)
        binding.viewModel = viewModel

        startCamera()

        return binding.root
    }

    private fun startCamera() {
        preview.setOnPreviewOutputUpdateListener {
            val parent = binding.cameraPreview.parent as ViewGroup
            parent.removeView(binding.cameraPreview)
            parent.addView(binding.cameraPreview, 0)

            binding.cameraPreview.surfaceTexture = it.surfaceTexture
            updateTransform()
        }

        CameraX.bindToLifecycle(this, preview, viewModel.analyzerUseCase)
    }

    private fun updateTransform() {
        val matrix = Matrix()

        val centerX = binding.cameraPreview.width / 2f
        val centerY = binding.cameraPreview.height / 2f

        val rotationDegrees = when(binding.cameraPreview.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)

        binding.cameraPreview.setTransform(matrix)
    }

}


我试着像这样获取旋转方向:binding.root.display.rotation。但是它仍然给出了同样的错误。
以下是错误信息:java.lang.IllegalStateException: binding.cameraPreview.display must not be null

我也遇到了和你一样的问题。 - Divya Gupta
@DivyaGupta 你可以看一下我的新帖子。我之前已经解决了问题,但是忘记发布解决方案了。对此我感到抱歉。 - Emre Turan
2个回答

4
我在三个月后找到了原因。startCamera()必须在onViewCreated中调用。您不应该在onCreateView()中做任何事情,只需要填充布局即可。修复后的代码如下:
class CameraPreviewFragment : Fragment() {

    private lateinit var binding: CameraPreviewFragmentBinding
    private lateinit var viewModel: CameraPreviewViewModel

    private val previewConfig = PreviewConfig.Builder().build()
    private val preview = Preview(previewConfig)

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        binding = CameraPreviewFragmentBinding.inflate(inflater, container, false)
        binding.lifecycleOwner = this

        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        val viewModelFactory = CameraPreviewViewModelFactory((activity as MainActivity).faceIDUtils)
        viewModel =
            ViewModelProviders.of(this, viewModelFactory).get(CameraPreviewViewModel::class.java)
        binding.viewModel = viewModel

        startCamera()
    }

    private fun startCamera() {
        preview.setOnPreviewOutputUpdateListener {
            val parent = binding.cameraPreview.parent as ViewGroup
            parent.removeView(binding.cameraPreview)
            parent.addView(binding.cameraPreview, 0)

            binding.cameraPreview.surfaceTexture = it.surfaceTexture
            updateTransform()
        }

        CameraX.bindToLifecycle(this, preview, viewModel.analyzerUseCase)
    }

    private fun updateTransform() {
        val matrix = Matrix()

        val centerX = binding.cameraPreview.width / 2f
        val centerY = binding.cameraPreview.height / 2f

        val rotationDegrees = when (binding.cameraPreview.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)

        binding.cameraPreview.setTransform(matrix)
    }
}

我正在onViewCreated中启动相机,但PreviewView.getDisplay()仍然为null。有任何想法吗? - Astha Garg
检查相机权限,你在启动之前应该已经获得了授权。 - vicky
检查相机权限,你在启动之前应该已经获得了授权权限。 - undefined

3

另一种选项,在我这种情况下,我是从一个Activity开始的,可以使用`View.post { startCamera() }`,因为在视图准备好之前,updateTransform被访问。


这正是在相机示例应用程序中所做的事情:https://github.com/android/camera-samples/blob/18cb7e16cc7150c1a200d2ef3f0c2048e9558d2c/CameraXBasic/app/src/main/java/com/android/example/cameraxbasic/fragments/CameraFragment.kt#L210 - guillaume-tgl

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