How to auto-focus with Android CameraX
Just point out, to get the "Tap to focus" working with PreviewView, you need to use DisplayOrientedMeteringPointFactory. Otherwise you'll get messed up coordinates.
val factory = DisplayOrientedMeteringPointFactory(activity.display, camera.cameraInfo, previewView.width.toFloat(), previewView.height.toFloat())
For the rest use the MatPag's answer.
There is an issue with some android devices where the camera's aren't auto-focusing with CameraX. The CameraX team is aware of it and are tracking it with an internal ticket and hopefully will have a fix soon.
With the current CameraX 1.0.0
, you can proceed in this 2 ways:
Auto-focus every X seconds:
previewView.afterMeasured { val autoFocusPoint = SurfaceOrientedMeteringPointFactory(1f, 1f) .createPoint(.5f, .5f) try { val autoFocusAction = FocusMeteringAction.Builder( autoFocusPoint, FocusMeteringAction.FLAG_AF ).apply { //start auto-focusing after 2 seconds setAutoCancelDuration(2, TimeUnit.SECONDS) }.build() camera.cameraControl.startFocusAndMetering(autoFocusAction) } catch (e: CameraInfoUnavailableException) { Log.d("ERROR", "cannot access camera", e) } }
Focus on-tap:
previewView.afterMeasured { previewView.setOnTouchListener { _, event -> return@setOnTouchListener when (event.action) { MotionEvent.ACTION_DOWN -> { true } MotionEvent.ACTION_UP -> { val factory: MeteringPointFactory = SurfaceOrientedMeteringPointFactory( previewView.width.toFloat(), previewView.height.toFloat() ) val autoFocusPoint = factory.createPoint(event.x, event.y) try { camera.cameraControl.startFocusAndMetering( FocusMeteringAction.Builder( autoFocusPoint, FocusMeteringAction.FLAG_AF ).apply { //focus only when the user tap the preview disableAutoCancel() }.build() ) } catch (e: CameraInfoUnavailableException) { Log.d("ERROR", "cannot access camera", e) } true } else -> false // Unhandled event. } } }
afterMeasured extension function is a simple utility: (thanks ch271828n for improving it)
inline fun View.afterMeasured(crossinline block: () -> Unit) {
if (measuredWidth > 0 && measuredHeight > 0) {
block()
} else {
viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.GlobalLayoutListener {
override fun onGlobalLayout() {
if (measuredWidth > 0 && measuredHeight > 0) {
viewTreeObserver.removeOnGlobalLayoutListener(this)
block()
}
}
})
}
}
A Camera
object can be obtained with
val camera = cameraProvider.bindToLifecycle(
this@Activity, cameraSelector, previewView //this is a PreviewView
)