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.
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
}
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
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()
).
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.