Implement a preview

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

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

Note: If you require direct access to the SurfaceTexture, such as to perform OpenGL rendering, see Manually create a SurfaceTexture.

Use the PreviewView

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

  1. Configure a CameraXConfig.Provider.
  2. Add a PreviewView to your layout.
  3. Request a CameraProvider.
  4. On View creation, check for the CameraProvider.
  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 PreviewSurfaceProvider.
  • Retrieve the SurfaceTexture from TextureView and set it on PreviewSurfaceProvider.
  • Get the Surface from SurfaceView and set it on PreviewSurfaceProvider.

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

Configure a CameraXConfig.Provider

Have your Application class implement the CameraXConfig.Provider interface, as shown in the following sample:

Kotlin

import androidx.camera.camera2.Camera2Config
import androidx.camera.core.CameraXConfig

public class MyCameraXApplication : Application(),  CameraXConfig.Provider {
  override fun getCameraXConfig(): CameraXConfig {
    return Camera2Config.defaultConfig(this)
  }
}

Java

import androidx.camera.camera2.Camera2Config;
import androidx.camera.core.CameraXConfig;

public class MyCameraXApplication extends Application implements CameraXConfig.Provider {
    @NonNull
    @Override
    public CameraXConfig getCameraXConfig() {
        return Camera2Config.defaultConfig(this);
    }
}

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/preview_view" />
</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
    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 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 code in this section shows how to do this.

For an example of the bindPreview function in the following sample, see the code provided in Select a camera and bind the lifecycle and use cases.

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));

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 and connect it to the PreviewView.
  2. Specify the desired camera LensFacing option.
  3. Bind the selected camera and any use cases to the lifecycle.

The following code shows an example:

Kotlin

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

    preview.setPreviewSurfaceProvider(previewView.getPreviewSurfaceProvider())

    var cameraSelector : CameraSelector = CameraSelector.Builder()
            .requireLensFacing(LensFacing.BACK)
            .build()

    cameraProvider.bindToLifecycle(PreviewViewFragment.this, cameraSelector, preview)
}

Java

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

    preview.setPreviewSurfaceProvider(previewView.getPreviewSurfaceProvider());

    CameraSelector cameraSelector =
            new CameraSelector.Builder().requireLensFacing(LensFacing.BACK).build();
    cameraProvider.bindToLifecycle(PreviewViewFragment.this, cameraSelector, preview);
}

Manually create a SurfaceTexture

While using a PreviewView is the best option in most cases, there are times when you might require access to a SurfaceTexture, such as to perform OpenGL rendering.

In these cases, pass in a SurfaceTexture to a Preview object using Preview.PreviewSurfaceProvider.

Kotlin

// Retrieve an instance of the camera provider
val cameraProvider = ...

val preview = Preview.Builder().build()

preview.setPreviewSurfaceProvider {
    resolution, surfaceReleaseFuture ->
    // Create the SurfaceTexture with the required resolution
    val surfaceTexture = SurfaceTexture(0)
    surfaceTexture.setDefaultBufferSize(resolution.width, resolution.height)
    val surface = Surface(surfaceTexture)

    // Once surfaceReleaseFuture completes, the Surface and SurfaceTexture
    // are no longer used by the camera hence safe to close.
    surfaceReleaseFuture.addListener(
        Runnable {
            surface.release()
            surfaceTexture.release()
        }, ContextCompat.getMainExecutor(this))

    // Return the Surface back in a ListenableFuture
        Futures.immediateFuture(surface)
}

cameraProvider.bindToLifecycle(this, cameraSelector, preview)

Java

// Retrieve an instance of the camera provider
LifecycleCameraProvider cameraProvider = ...;

Preview preview = new Preview.Builder().build();

preview.setPreviewSurfaceProvider((resolution, surfaceReleaseFuture) -> {
    // Create the SurfaceTexture with the required resolution
    SurfaceTexture surfaceTexture = new SurfaceTexture(0);
    surfaceTexture.setDefaultBufferSize(resolution.getWidth(), resolution.getHeight());
    Surface surface = new Surface(surfaceTexture);

    // Once surfaceReleaseFuture completes, the Surface and SurfaceTexture
    // are no longer used by the camera hence safe to close.
    surfaceReleaseFuture.addListener(() -> {
            surface.release();
            surfaceTexture.release();
        }, ContextCompat.getMainExecutor(this));

    // Return the Surface back in a ListenableFuture
    return Futures.immediateFuture(surface);
});

cameraProvider.bindToLifecycle((LifecycleOwner) this, preview);

Caution: The SurfaceTexture must conform to the resolution and lifespan requirements specified by the PreviewSurfaceProvider. Failure to do so might result in undefined behavior, up to and including an application crash.

Additional resources

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

Codelab

  • Getting Started with CameraX
  • Code sample

  • Official CameraX sample app