如何在 CameraX 中镜像 PreviewView?

How to mirror the PreviewView in CameraX?

免责声明:我知道 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
}

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

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

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