Android has released a new API camerax in recent months. I'm trying to understand how to get auto-focusing for the camera to work.
https://groups.google.com/a/android.com/forum/#!searchin/camerax-developers/auto$20focus|sort:date/camerax-developers/IQ3KZd8iOIY/LIbrRIqEBgAJ
Here is a discussion on the topic but there is almost no specific documentation on it.
https://github.com/android/camera-samples/tree/master/CameraXBasic/app/src/main/java/com/android/example/cameraxbasic
Here is also the basic camerax app but I couldn't find any file dealing with the auto focusing.
Any tips or points to documentation is helpful. Also I'm fairly new to android so its very possible I'm missing something that makes the above links more useful.
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
)
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.
With current 1.0.0-rc03
and 1.0.0-alpha22
artifacts
This solution assumes that camera is already setup including bindToLifecycle
. After that we need to check whether previewView streamState is STREAMING before trying to focus the camera
previewView.getPreviewStreamState().observe(getActivity(), value -> {
if (value.equals(STREAMING)) {
setUpCameraAutoFocus();
}
});
private void setUpCameraAutoFocus() {
final float x = previewView.getX() + previewView.getWidth() / 2f;
final float y = previewView.getY() + previewView.getHeight() / 2f;
MeteringPointFactory pointFactory = previewView.getMeteringPointFactory();
float afPointWidth = 1.0f / 6.0f; // 1/6 total area
float aePointWidth = afPointWidth * 1.5f;
MeteringPoint afPoint = pointFactory.createPoint(x, y, afPointWidth);
MeteringPoint aePoint = pointFactory.createPoint(x, y, aePointWidth);
ListenableFuture<FocusMeteringResult> future = cameraControl.startFocusAndMetering(
new FocusMeteringAction.Builder(afPoint,
FocusMeteringAction.FLAG_AF).addPoint(aePoint,
FocusMeteringAction.FLAG_AE).build());
Futures.addCallback(future, new FutureCallback<FocusMeteringResult>() {
@Override
public void onSuccess(@Nullable FocusMeteringResult result) {
}
@Override
public void onFailure(Throwable t) {
// Throw the unexpected error.
throw new RuntimeException(t);
}
}, CameraXExecutors.directExecutor());
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With