如何在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.