Display a camera preview with PreviewView
Android Jetpack CameraX: PreviewView
A common use case for any camera app is to display a preview from the camera. So far, this has been quite difficult to get right, mostly due to the complexities that exist around the camera2 API edge cases and varying device behaviors. PreviewView
, part of the CameraX Jetpack library, makes displaying a camera preview easier for developers by providing a developer-friendly, consistent, and stable API across a wide range of Android devices.
Introducing PreviewView
PreviewView
is a custom View
that enables the display of a camera feed. It was built to offload the burden of setting up and handling the preview surface used by the camera.
If you need to display a basic camera preview in your app, PreviewView
is the recommended way to do so, as it is:
- Easier to use:
PreviewView
is aView
. It implements all the work necessary to display what the camera sees in your layout by managing theSurface
used by thePreview
use case. - Light-weight:
PreviewView
focuses only on preview. All its internal resources are geared towards getting a camera preview set up and managing the preview surface while the camera’s using it. This separation of concerns allows forPreviewView
code to stay clean. - Exhaustive:
PreviewView
handles the hardest parts of displaying the camera feed on the screen, including aspect ratio, scaling, and rotation. It also contains compatibility fixes and workarounds in order to provide a seamless experience on a multitude of devices, screen sizes, camera hardware support levels, and display configurations such as split-screen mode, locked orientation, and free-form multi-window.
PreviewView — Implementation modes
PreviewView
is a subclass of FrameLayout
. To display the camera feed, it uses either a SurfaceView
or TextureView
, provides a preview surface to the camera when it’s ready, tries to keep it valid as long as the camera is using it, and when released prematurely, provides a new surface if the camera is still in use.
SurfaceView
is generally better than TextureView
when it comes to certain key metrics, including power and latency, which is why PreviewView
tries to use a SurfaceView
by default. However, some devices (mainly legacy devices) crash when the preview surface is released prematurely. With SurfaceView
, unfortunately, it isn’t possible to control when the surface is released, as this is controlled by the View
hierarchy. On these devices, PreviewView
falls back to using a TextureView
instead. You should also force PreviewView
to use a TextureView
in cases where preview rotation, transparency, or animation are needed.
You can explicitly set the implementation you want PreviewView
to use by calling PreviewView.setPreferredImplementationMode(ImplementationMode)
, where ImplementationMode
is either SURFACE_VIEW
or TEXTURE_VIEW
. PreviewView
tries to honor your choice when the preferred mode is SURFACE_VIEW
, and guarantees it when it’s TEXTURE_VIEW
.
⚠️ Make sure to set your preferred implementation mode before starting preview by calling Preview.setSurfaceProvider(PreviewView.createSurfaceProvider())
.
The following shows how to set the preferred implementation mode of PreviewView
.
PreviewView — Preview
PreviewView
handles the nuts and bolts of creating a SurfaceProvider
needed by the Preview
use case to start a preview stream. The SurfaceProvider
prepares the surface that will be provided to the camera in order to display a camera preview stream, and takes care of recreating the Surface
when necessary. PreviewView.createSurfaceProvider(CameraInfo)
accepts a nullable CameraInfo
instance. PreviewView
uses it, along with your preferred implementation mode and the camera’s capabilities, to determine the implementation to use internally. If you pass in a null
CameraInfo
, PreviewView
uses a TextureView
implementation, since it can’t tell whether the chosen camera will work with SurfaceView
.
Once you build the Preview
use case and any other use cases you need, bind them to a LifecycleOwner
, use the CameraInfo
from the bound camera to create a SurfaceProvider
, and then attach it to the Preview
use case to start the preview stream by calling Preview.setSurfaceProvider(SurfaceProvider)
.
The following shows how to attach PreviewView
to Preview
to start a preview stream.
PreviewView — Scale types
PreviewView
provides an API that allows you to control how the preview should look and where it should be positioned within its container.
- The how defines whether the preview should fit inside (FIT) or fill (FILL) its container.
- The where defines whether the preview should be at the top left (START), center (CENTER) or bottom right (END) or its container.
The combinations created from the “how” and “where” represent the available scale type values PreviewView
supports: FIT_START
, FIT_CENTER
, FIT_END
, FILL_START
, FILL_CENTER
and FILL_END
. The most commonly used are FIT_CENTER
, which translates to a letterboxed preview, and FILL_CENTER
which center-crops the preview in its container.
Setting the scale type can be done in one of two ways:
- In the XML layout by using
PreviewView
’sscaleType
attribute as shown in the following sample.
- Programmatically by calling
PreviewView.setScaleType(ScaleType)
as shown below.
To retrieve the current scale type that a PreviewView
is using, call PreviewView.getScaleType()
.
PreviewView — Camera Controls
Depending on the camera sensor orientation, the device’s rotation, the display mode, and the preview scale type, PreviewView
may scale, rotate, and translate the preview frames it receives from the camera in order to correctly display the preview stream in the UI. This is why being able to convert UI coordinates to camera sensor coordinates is important. In CameraX, this conversion is done by a MeteringPointFactory
. PreviewView
provides an API to create one: PreviewView.createMeteringPointFactory(cameraSelector)
, where the CameraSelector
portrays the camera streaming the preview.
PreviewView
’s MeteringPointFactory
comes in handy when you need to implement tap-to-focus. Even though autofocus is enabled by default on the camera preview (when the camera supports it), you may also control the focus target when tapping on PreviewView
. The MeteringPointFactory
will convert the coordinates of the focus target to the camera sensor coordinates, which then enables the camera to focus on that region.
The following sample shows how to implement tap-to-focus using a touch listener set on PreviewView
.
Another common camera preview feature is pinch-to-zoom, which enables the camera to zoom in and out as you pinch the preview. To implement it with PreviewView
, add a touch listener to PreviewView
and attach it to a scale gesture listener. This will intercept pinch gestures and update the camera’s zoom ratio accordingly.
The following sample shows how to implement pinch-to-zoom with PreviewView
.
PreviewView — How it’s tested
PreviewView
provides a consistent camera behavior across a wide range of Android devices. This is thanks to the investment CameraX has made in testing PreviewView
and its other APIs in its automated testing lab. These tests are divided into two main categories:
- Unit tests that verify
PreviewView
’s behavior with regard to its implementation modes, scale types, andMeteringPointFactory
. They also make surePreviewView
correctly adjusts the preview when it should, such as when the size of its container changes, when the display layout is updated, and when it is attached or reattached to aWindow
. - Integration tests that ensure
PreviewView
behaves correctly when it’s part of an app, and displays or stops a preview stream accordingly. These tests include verifying the preview state while the app is running, after it’s closed and reopened multiple times, after the camera lens is toggled back and forth, and after the app’s lifecycle is destroyed and recreated. Currently, these tests mostly cover theTextureView
implementation ofPreviewView
, as getting a signal on when the preview starts and stops from aSurfaceView
has proved to be challenging.
Conclusion
In summary:
PreviewView
is a customView
that makes displaying a camera preview easier.PreviewView
handles the preview surface using aSurfaceView
by default, but may fall back to using aTextureView
when needed or requested.- Bind your other use cases, like
ImageCapture
andImageAnalysis
, to aLifecycleOwner
, and then start the camera preview by attaching aSurfaceProvider
fromPreviewView
to the boundPreview
use case. - Control how the preview is displayed by defining
PreviewView
’s scale type. - Implement tap-to-focus on your preview by getting a
MeteringPointFactory
fromPreviewView
. - Implement pinch-to-zoom on your preview by setting up a gesture listener on
PreviewView
.
Want more CameraX goodness? Check out:
- Official CameraX documentation
- Guided CameraX introduction codelab
- CameraX developer community
- Android’s CameraX Jetpack Library is now in beta!
- What’s new in CameraX
- Sample camera app built using CameraX
Questions about anything related to PreviewView
or preview? Leave a comment. Thanks for reading!