如何在CameraX中镜像PreviewView?

问题描述:

免责声明:我知道存在这个问题,但是目前立场尚未解决,我正在尝试提供更多信息,而又不会污染那些无用的答案的问题,反正也无法解决问题.

Disclaimer: I'm aware of the existence of this question, but it currently stands unresolved and I'm trying to provide extra information without polluting that one with useless answers that won't solve the problem anyway.

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

I have a custom device with a front camera that is mirrored by default, so I want to display the preview normally and I need to horizontally flip the content of PreviewView, but I'm stuck. Other people in the past have suggested using PreviewView#setScaleX(-1) but it either doesn't work at all or it needs to be called at a very specific point in the code, which I haven't found yet.

下面的代码是官方

The code below is a simplified version of CameraFragment.kt in the official CameraXBasic example; I've added comments where I've already tired calling viewFinder.scaleX = -1f with no success. Honestly I don't really think that the place makes a difference because if I call it with any value other than 1 it works fine with both scaleX and scaleY, but it always ignores the negative sign so it never flips.

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 PERFORMANCE )以及设备 PreviewView 可以使用 TextureView SurfaceView ,在您的情况下,我假设 PreviewView 使用的是 SurfaceView ,您可以通过访问 PreviewView 的第一个子视图( View.getChildAt(0))来确认.

Depending on the implementation type (COMPATIBLE vs PERFORMANCE) and the device, PreviewView can either use a TextureView or a SurfaceView, in your case I'm assuming PreviewView is using a SurfaceView, you can confirm this by getting access to PreviewView's first child view (View.getChildAt(0)).

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

TextureView is just like any other View, which is why when PreviewView uses it, setting its scaleX to -1 should mirror the displayed preview. You can call PreviewView.setScaleX(-1F) once the layout is created (e.g. In onViewCreated()).

使用 SurfaceView 有点棘手,因为 Surface View 在某些方面是独立的 : Surface 放置在保存 View 的窗口后面,并且View层次结构通过在窗口上打孔以显示 SurfaceView Surface .这也许可以解释为什么无法镜像内容 SurfaceView 显示,尽管我不确定为什么要放大(scaleX设置为值> 1)和缩小(scaleX设置为0到1之间的值))仍然可以工作.

With SurfaceView, it's a bit tricky, as the Surface and the View are independent in some aspects: The Surface is placed behind the window that holds the View, and the View hierarchy handles correctly composing the entire layout by punching a hole in the window in order to display SurfaceView's Surface. This might explain why mirroring the content SurfaceView displays isn't possible, though I'm not sure why zooming in (scaleX set to values > 1) and out (scaleX set to values between 0 and 1) work nonetheless.