Implement a preview

When adding a preview to your app, use PreviewView, which is a View that can be cropped, scaled, and rotated for proper display.

The image preview streams to a surface inside the PreviewView when the camera becomes active.

Use the PreviewView

Implementing a preview for CameraX using PreviewView involves the following steps, which are covered in later sections:

  1. Optionally configure a CameraXConfig.Provider.
  2. Add a PreviewView to your layout.
  3. Request a ProcessCameraProvider.
  4. On View creation, check for the ProcessCameraProvider.
  5. Select a camera and bind the lifecycle and use cases.

Using PreviewView has some limitations. When using PreviewView, you can't do any of the following things:

  • Create a SurfaceTexture to set on TextureView and Preview.SurfaceProvider.
  • Retrieve the SurfaceTexture from TextureView and set it on Preview.SurfaceProvider.
  • Get the Surface from SurfaceView and set it on Preview.SurfaceProvider.

If any of these happen, then the Preview stops streaming frames to the PreviewView.

Add a PreviewView to your layout

The following sample shows a PreviewView in a layout:

<FrameLayout
    android:id="@+id/container">
        <androidx.camera.view.PreviewView
            android:id="@+id/previewView" />
</FrameLayout>

Request a CameraProvider

The following code shows how to request a CameraProvider:

Kotlin

import androidx.camera.lifecycle.ProcessCameraProvider
import com.google.common.util.concurrent.ListenableFuture

class MainActivity : AppCompatActivity() {
    private lateinit var cameraProviderFuture : ListenableFuture<ProcessCameraProvider>
    override fun onCreate(savedInstanceState: Bundle?) {
        cameraProviderFuture = ProcessCameraProvider.getInstance(this)
    }
}

Java

import androidx.camera.lifecycle.ProcessCameraProvider
import com.google.common.util.concurrent.ListenableFuture

public class MainActivity extends AppCompatActivity {
    private ListenableFuture<ProcessCameraProvider> cameraProviderFuture;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        cameraProviderFuture = ProcessCameraProvider.getInstance(this);
    }
}

Check for CameraProvider availability

After requesting a CameraProvider, verify that its initialization succeeded when the view is created. The following code shows how to do this:

Kotlin

cameraProviderFuture.addListener(Runnable {
    val cameraProvider = cameraProviderFuture.get()
    bindPreview(cameraProvider)
}, ContextCompat.getMainExecutor(this))

Java

cameraProviderFuture.addListener(() -> {
    try {
        ProcessCameraProvider cameraProvider = cameraProviderFuture.get();
        bindPreview(cameraProvider);
    } catch (ExecutionException | InterruptedException e) {
        // No errors need to be handled for this Future.
        // This should never be reached.
    }
}, ContextCompat.getMainExecutor(this));

For an example of the bindPreview function used in this sample, see the code provided in the next section.

Select a camera and bind the lifecycle and use cases

Once you have created and confirmed the CameraProvider, do the following:

  1. Create a Preview.
  2. Specify the desired camera LensFacing option.
  3. Bind the selected camera and any use cases to the lifecycle.
  4. Connect the Preview to the PreviewView.

The following code shows an example:

Kotlin

fun bindPreview(cameraProvider : ProcessCameraProvider) {
    var preview : Preview = Preview.Builder()
            .build()

    var cameraSelector : CameraSelector = CameraSelector.Builder()
          .requireLensFacing(CameraSelector.LENS_FACING_BACK)
          .build()

    preview.setSurfaceProvider(previewView.getSurfaceProvider())

    var camera = cameraProvider.bindToLifecycle(this as LifecycleOwner, cameraSelector, preview)
}

Java

void bindPreview(@NonNull ProcessCameraProvider cameraProvider) {
    Preview preview = new Preview.Builder()
            .build();

    CameraSelector cameraSelector = new CameraSelector.Builder()
            .requireLensFacing(CameraSelector.LENS_FACING_BACK)
            .build();

    preview.setSurfaceProvider(previewView.getSurfaceProvider());

    Camera camera = cameraProvider.bindToLifecycle((LifecycleOwner)this, cameraSelector, preview);
}

Note that bindToLifecycle() returns a Camera object. For more information about controlling camera output, such as zoom and exposure, see Camera output.

You are now done implementing the camera preview. Build your app and confirm that your preview appears in your app and functions as you intend it to.

Additional controls for PreviewView

CameraX PreviewView provides some additional APIs to configure properties such as:

Implementation mode

PreviewView can use one of the following modes to render a preview stream onto the target View:

  • PERFORMANCE is the default mode. PreviewView uses a SurfaceView to display the video stream, but falls back to a TextureView in certain cases. SurfaceView has a dedicated drawing surface, which has a better chance of being implemented with a hardware overlay by the internal hardware compositor, especially when there are no other UI elements (like buttons) on top of the preview video. By rendering with a hardware overlay, video frames avoid a GPU path, which can reduce platform power consumption and latency.

  • COMPATIBLE mode. In this mode, PreviewView uses a TextureView which, unlike SurfaceView, does not have a dedicated drawing surface. As a result, video renders with blending so that it can be displayed. During this extra step, the application can perform additional processing, such as scaling and rotating videos without restriction.

Use PreviewView.setImplementationMode() to select the implementation mode suitable for your application. If the default PERFORMANCE mode isn't suitable for your application, the following code sample shows how to set COMPATIBLE mode:

Kotlin

// viewFinder is a PreviewView instance
viewFinder.implementationMode = PreviewView.ImplementationMode.COMPATIBLE

Scale type

When the preview video resolution differs from the dimensions of your target PreviewView, video content needs to be fit to the view either by cropping or letterboxing (maintaining the original aspect ratio). PreviewView provides the following ScaleTypes for this purpose:

  • FIT_CENTER, FIT_START, and FIT_END for letterboxing. The full video content is scaled (either up or down) to the maximum possible size that can be displayed in the target PreviewView. However, although the full video frame is visible, some portion of the screen might be blank. Depending on which of these three scale types you choose, the video frame aligns to the center, start, or end of the target View.

  • FILL_CENTER, FILL_START, FILL_END for cropping. If a video doesn't match the PreviewView aspect ratio, only a portion of the content is visible, but the video fills the entire PreviewView.

The default scale type CameraX uses is FILL_CENTER. Use PreviewView.setScaleType() to set the scale type most appropriate for your application. The following code sample sets the FIT_CENTER scale type:

Kotlin

// viewFinder is a PreviewView instance
viewFinder.scaleType = PreviewView.ScaleType.FIT_CENTER

The process for displaying a video consists of the following steps:

  1. Scale the video:
    • For FIT_* scale types, scale the video with min(dst.width/src.width, dst.height/src.height).
    • For FILL_* scale types, scale the video with max(dst.width/src.width, dst.height/src.height).
  2. Align the scaled video with the destination PreviewView:
    • For FIT_CENTER/FILL_CENTER, center align the scaled video and the destination PreviewView.
    • For FIT_START/FILL_START, align the scaled video and the destination PreviewView with respect to the top-left corner of each.
    • For FIT_END/FILL_END, align the scaled video and the destination PreviewView with respect to the bottom-right corner of each.

For example, here is a 640x480 source video and a 1920x1080 destination PreviewView:

Image showing a 640x480 video compared to a 1920x1080 preview

The following image shows the FIT_START / FIT_CENTER / FIT_END scaling process:

Image showing the FIT_START, FIT_CENTER, and FIT_END scaling process

The process works like this:

  1. Scale the video frame (maintaining the original aspect ratio) with min(1920/640, 1080/480) = 2.25 to get an intermediate video frame of 1440x1080.
  2. Align the 1440x1080 video frame with the 1920x1080 PreviewView.
    • For FIT_CENTER, align the video frame with the center of the PreviewView window. The starting and ending 240 pixel columns of the PreviewView are blank.
    • For FIT_START, align the video frame with the start (top-left corner) of the PreviewView window. The ending 480 pixel columns of the PreviewView are blank.
    • For FIT_END, align the video frame with the end (bottom-right corner) of the PreviewView window. The starting 480 pixel columns of the PreviewView are blank.

The following image shows the FILL_START / FILL_CENTER / FILL_END scaling process:

Image showing the FILL_START, FILL_CENTER, and FILL_END scaling process

The process works like this:

  1. Scale the video frame with max(1920/640, 1080/480) = 3 to get an intermediate video frame of 1920x1440 (which is larger than the size of the PreviewView).
  2. Crop the 1920x1440 video frame to fit the 1920x1080 PreviewView window.
    • For FILL_CENTER, crop 1920x1080 from the center of the 1920x1440 scaled video. The top and bottom 180 lines of video are not visible.
    • For FILL_START, crop 1920x1080 from the start of the 1920x1440 scaled video. The bottom 360 lines of video are not visible.
    • For FILL_END, crop 1920x1080 from the end of the 1920x1440 scaled video. The top 360 lines of video are not visible.

Additional resources

To learn more about CameraX, see the following additional resources.

Codelab

  • Getting Started with CameraX
  • Code sample

  • CameraX sample apps