如何在 CameraX 中镜像 PreviewView?

问题描述

免责声明:我知道 this question 的存在,但它目前尚未解决,我正在尝试提供额外的信息,而不会用无法解决问题的无用答案污染那个信息无论如何。

我有一个带有前置摄像头的自定义设备,认情况下是镜像的,所以我想正常显示预览,我需要水平翻转PreviewView的内容,但我卡住了。过去其他人曾建议使用 PreviewView#setScaleX(-1),但它要么根本不起作用,要么需要在代码中非常特定的位置调用,而我还没有找到。

下面的代码是官方CameraXBasic示例中CameraFragment.kt的简化版本;我在已经厌倦了调用 viewFinder.scaleX = -1f 却没有成功的地方添加评论。老实说,我真的不认为这个地方有什么不同,因为如果我用 1 以外的任何值调用它,它对 scaleXscaleY 都可以正常工作,但是 它总是忽略负值标志,所以它永远不会翻转。

private lateinit var viewFinder: PreviewView

override fun onViewCreated(view: View,savedInstanceState: Bundle?) {
    ...
    viewFinder = view.findViewById(R.id.view_finder)
    // HERE
    viewFinder.post {
        // HERE
        setupCamera()
    }
}

private fun setupCamera() {
    val cameraProviderFuture = ProcessCameraProvider.getInstance(requireContext())
    cameraProviderFuture.addListener(Runnable {
        val cameraProvider = cameraProviderFuture.get()

        val cameraSelector = CameraSelector.Builder()
            .requireLensFacing(CameraSelector.LENS_FACING_FRONT)
            .build()

       val preview = Preview.Builder()
            .build()
            .also {
                // HERE
                it.setSurfaceProvider(viewFinder.surfaceProvider)
            }

       cameraProvider.unbindAll()

        try {
            cameraProvider.bindToLifecycle(this,cameraSelector,preview)
            // HERE
        } catch (exc: Exception) {
            Log.e(TAG,"Use case binding Failed",exc)
        }
        // HERE
    },ContextCompat.getMainExecutor(requireContext()))
    // HERE
}

解决方法

根据实现类型(COMPATIBLEPERFORMANCE)和设备,PreviewView 可以使用 TextureViewSurfaceView,在您的情况下我假设 PreviewView 正在使用 SurfaceView,您可以通过访问 PreviewView 的第一个子视图 (View.getChildAt(0)) 来确认这一点。

TextureView 就像任何其他 View 一样,这就是为什么当 PreviewView 使用它时,将其 scaleX 设置为 -1 应该反映显示的预览。创建布局后(例如在 PreviewView.setScaleX(-1F) 中),您可以调用 onViewCreated()

对于 SurfaceView,它有点棘手,因为 SurfaceView 在某些方面独立Surface 被放置在包含 View 的窗口后面,View 层次结构通过在窗口中打一个洞来正确处理整个布局,以显示 SurfaceViewSurface。这可能解释了为什么无法镜像内容 SurfaceView 显示,尽管我不确定为什么放大(scaleX 设置为值 > 1)和缩小(scaleX 设置为 0 和 1 之间的值)仍然有效。