CameraX: Learn how to use CameraController
CameraX currently provides CameraView
, a View
that displays a preview of the camera, while also providing methods to take pictures, control the camera, and query camera information. CameraView
clearly takes on more responsibilities than a View
should, as it participates in the View
hierarchy and displays content, while also owning camera resources that exist outside the scope and lifecycle of the View
hierarchy.
CameraView
violates the separation of concerns principle, making it harder to guarantee its robustness in all corner cases. As a result, it will be marked as deprecated and removed before CameraX’s view artifact reaches Beta. It has been split up into two parts that you should use instead: PreviewView
which handles its view-related tasks, and CameraController
which handles the camera operations.
PreviewView
has been available in CameraX’s view artifact for a while. CameraController
, however, was introduced recently in version alpha19. It’s a high-level, all-in-one API that provides a way to easily access and manipulate core camera features, including displaying a camera preview, taking a picture, and analyzing camera frames. It does so while matching the output of its use cases to the viewfinder’s preview, thus providing a “What You See Is What You Get” (WYSIWYG) experience, a highly requested feature by developers that makes CameraX’s usage very intuitive. It also takes care of initializing the camera and adapting its output to device rotation.
To offload the burden of starting, stopping, and closing the camera, CameraX also introduced LifecycleCameraController
, which offers all the convenience of CameraController
with the added benefit of binding the camera’s lifecycle to that of a LifecycleOwner
, typically the lifecycle of a Fragment
or Activity
. This allows the lifecycle’s state to control when the camera is opened, stopped, and closed.
Using PreviewView
with CameraController
is fairly simple, and can be done as follows:
The following sections list the mappings from CameraView
to CameraController
. You can use them to migrate from CameraView
to PreviewView
and CameraController
.
Camera Initialization
Unlike CameraView
, CameraController
provides a ListenableFuture
to monitor its initialization. It’s an asynchronous operation during which CameraController
initializes CameraX and binds its use cases. The benefit of using this ListenableFuture
is twofold:
- Once it successfully completes, you can allow your users to start interacting with the camera, for example by taking pictures and zooming in and out of the viewfinder.
- In case it fails, you can gracefully handle the error and communicate it to your users.
In case you aren’t familiar with ListenableFuture
: it wraps an asynchronous operation and allows you to attach a listener that’s invoked on its completion. In case the operation has already finished, the future returns immediately.
Camera Lifecycle
Similar to CameraView
, LifecycleCameraController
ties control of the camera to a lifecycle. You must bind a valid LifecycleOwner
to the controller for it to be operational.
LifecycleCameraController
provides an additional unbind()
method which allows you to prematurely close the camera before the lifecycle that it’s bound to comes to an end. This is useful in cases in which the camera isn’t integral to a screen and isn’t needed for its entire lifecycle. Alternatively, you can define your custom LifecycleOwner
to control when to close the camera, but unbind()
can be more convenient in some cases.
Camera Control
- Zoom
CameraController
allows you to observe the camera’s zoom state via a LiveData
instance. This state becomes available once the camera is open, and holds both static information, like the maximum and minimum zoom ratios, and dynamic information, like the current zoom ratio. You can verify whether zoom is supported using the maximum zoom ratio as follows:
CameraController
additionally provides methods to get and set linear zoom, which can vary between 0 (minimum zoom) and 1 (maximum zoom).
Finally, like CameraView
, CameraController
allows you to enable or disable pinch-to-zoom. When enabled, CameraController
handles pinch gestures on its attached PreviewView
to zoom the camera in and out.
- Flash/Torch
CameraController
provides similar methods as CameraView
to set and query the image capture’s flash mode, but unlike CameraView
, it allows observing changes in the camera’s torch state via a LiveData
instance, which emits TorchState.OFF
and TorchState.ON
.
- Focus/Metering
While CameraView
automatically acquires focus on any region of the viewfinder that is tapped, CameraController
provides more flexibility by allowing you to enable or disable tap-to-focus functionality. When enabled, CameraController
handles touch events on its attached PreviewView
by focusing the camera on tapped regions.
Camera Selection
CameraController
provides you with more control over which camera to use by allowing you to specify a CameraSelector
to select the camera it uses. This allows you to rotate between various cameras when toggling cameras, instead of only the default front and back cameras, which was a limitation of CameraView.toggleCamera()
. You can still implement this fairly easily, though, using CameraController
as follows.
Camera Use Cases
CameraController
supports all of CameraX’s use cases: Preview
, ImageAnalysis
, and ImageCapture
, thus making it a more robust camera solution compared to CameraView
. It also matches the output of ImageAnalysis
and ImageCapture
to the preview’s display, thereby providing a WYSIWYG experience.
CameraController
’s default state enables preview, image analysis, and image capture. The Preview
use case is always enabled, so you can choose the remaining use cases to enable or disable depending on your camera usage needs.
- Preview
Unlike CameraView
which wraps (and thus hides) a PreviewView
instance and forwards method calls to it, CameraController
is decoupled from PreviewView
. This means you have more control over the viewfinder, and can access and manipulate it directly.
- ImageAnalysis
Unlike CameraView
, CameraController
supports the ImageAnalysis
use case. You can set and clear its Analyzer
, while also configuring its image-processing pipeline.
- ImageCapture
CameraController
provides the same methods as CameraView
to take a picture, specify the image save destination, and provide the image capture callbacks.
One thing to note is that CameraController
mirrors an image captured with a front-facing camera, unless you disable this in the OutputFileOptions
’s metadata by calling Metadata.setReversedHorizontal(false)
.
Conclusion
In summary:
- Because
CameraView
is handling the responsibilities of both a view and a controller in the MVC sense, CameraX is deprecating it and splitting it intoPreviewView
and the newly introducedCameraController
. CameraController
handles camera initialization, as well as the creation and configuration of its use cases.CameraController
provides a WYSIWYG experience by matching the output of its use cases toPreviewView
’s display.CameraController
listens to the device’s motion sensor to correctly rotate the output of its use cases.CameraController
adds support for theImageAnalysis
use case, making it a more robust camera solution that provides easy access to all of CameraX’s use cases.CameraController
supports all ofCameraView
’s features and more, such as enabling and disabling tap-to-focus, getting and setting linear zoom, and observing dynamic camera information like the zoom and torch states.LifecycleCameraController
is aCameraController
that binds the camera’s lifecycle to that of aLifecycleOwner
, typically the lifecycle of the UI.
Want more CameraX goodness? Check out: