实现预览

在向应用添加预览时,请使用 PreviewView,这是一种可以剪裁、缩放和旋转以确保正确显示的 View

当相机处于活动状态时,图片预览会流式传输到 PreviewView 中的 Surface。

使用 PreviewView

如需使用 PreviewView 实现 CameraX 预览,请按以下步骤操作(稍后将对这些步骤进行说明):

  1. (可选)配置 CameraXConfig.Provider
  2. PreviewView 添加到布局。
  3. 请求 ProcessCameraProvider
  4. 在创建 View 时,请检查 ProcessCameraProvider
  5. 选择相机并绑定生命周期和用例。

使用 PreviewView 存在一些限制。使用 PreviewView 时,您无法执行以下任何操作:

  • 创建 SurfaceTexture,以在 TextureViewPreview.SurfaceProvider 上进行设置。
  • TextureView 检索 SurfaceTexture,并在 Preview.SurfaceProvider 上对其进行设置。
  • SurfaceView 获取 Surface,并在 Preview.SurfaceProvider 上对其进行设置。

如果出现上述任何一种情况,Preview 就会停止将帧流式传输到 PreviewView

将 PreviewView 添加到布局

以下示例显示了布局中的 PreviewView

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

请求 CameraProvider

以下代码展示了如何请求 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);
    }
}

检查 CameraProvider 可用性

请求 CameraProvider 后,请验证它能否在视图创建后成功初始化。以下代码展示了如何执行此操作:

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

如需查看此示例中使用的 bindPreview 函数的示例,请参阅下一部分中提供的代码。

选择相机并绑定生命周期和用例

创建并确认 CameraProvider 后,请执行以下操作:

  1. 创建 Preview
  2. 指定所需的相机 LensFacing 选项。
  3. 将所选相机和任意用例绑定到生命周期。
  4. Preview 连接到 PreviewView

以下代码展示了一个示例:

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

请注意,bindToLifecycle() 会返回一个 Camera 对象。如需详细了解如何控制相机输出(如变焦和曝光),请参阅此指南

现在,您已完成实现相机预览的操作。请构建您的应用,然后确认预览是否出现在您的应用中并能按预期工作。

PreviewView 的其他控件

CameraX PreviewView 提供了一些其他 API 来用于配置属性,例如:

实现模式

PreviewView 可以使用以下模式之一将预览流渲染到目标 View 上:

  • PERFORMANCE 是默认模式。PreviewView 会使用 SurfaceView 显示视频串流,但在某些情况下会回退为使用 TextureViewSurfaceView 具有专用的绘图界面,该对象更有可能通过内部硬件合成器实现硬件叠加层,尤其是当预览视频上面没有其他界面元素(如按钮)时。通过使用硬件叠加层进行渲染,视频帧会避开 GPU 路径,从而能降低平台功耗并缩短延迟时间。

  • COMPATIBLE 模式。在此模式下,PreviewView 会使用 TextureView;不同于 SurfaceView,该对象没有专用的绘图表面。因此,视频要通过混合渲染,才能显示。在这个额外的步骤中,应用可以执行额外的处理工作,例如不受限制地缩放和旋转视频。

您可以使用 PreviewView.setImplementationMode() 选择适合具体应用的实现模式。如果默认的 PERFORMANCE 模式不适合您的应用,请参阅以下代码示例,了解如何设置 COMPATIBLE 模式:

Kotlin

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

缩放类型

当预览视频分辨率与目标 PreviewView 的尺寸不同时,视频内容需要通过剪裁操作或添加遮幅式黑边来适应视图(保持原始宽高比)。为此,PreviewView 提供了以下 ScaleTypes

  • FIT_CENTERFIT_STARTFIT_END,用于添加遮幅式黑边。整个视频内容会调整(放大或缩小)为可在目标 PreviewView 中显示的最大尺寸。不过,虽然整个视频帧会完整显示,但屏幕画面中可能会出现空白部分。视频帧会与目标视图的中心、起始或结束位置对齐,具体取决于您在上述三种缩放类型中选择了哪一种。

  • FILL_CENTERFILL_STARTFILL_END,用于进行剪裁。如果视频的宽高比与 PreviewView 不匹配,画面中只会显示部分内容,但视频仍会填满整个 PreviewView

CameraX 使用的默认缩放类型是 FILL_CENTER。您可以使用 PreviewView.setScaleType() 设置最适合具体应用的缩放类型。下面的代码示例设置了 FIT_CENTER 缩放类型:

Kotlin

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

视频显示过程包括以下步骤:

  1. 缩放视频:
    • 如果您选择的缩放类型是 FIT_*,请使用 min(dst.width/src.width, dst.height/src.height) 缩放视频。
    • 如果您选择的缩放类型是 FILL_*,请使用 max(dst.width/src.width, dst.height/src.height) 缩放视频。
  2. 将经过缩放的视频与目标 PreviewView 对齐:
    • 对于 FIT_CENTER/FILL_CENTER,请将经过缩放的视频与目标 PreviewView 居中对齐。
    • 对于 FIT_START/FILL_START,请以每个视频的左上角为准,将经过缩放的视频与目标 PreviewView 对齐。
    • 对于 FIT_END/FILL_END,请以每个视频的右下角为准,将经过缩放的视频与目标 PreviewView 对齐。

例如,下面是 640x480 的源视频和 1920x1080 的目标 PreviewView

显示 640x480 视频与 1920x1080 预览相对比的图片

下图显示了 FIT_START / FIT_CENTER / FIT_END 缩放过程:

显示 FIT_START、FIT_CENTER 和 FIT_END 缩放过程的图片

该工作流程如下:

  1. 使用 min(1920/640, 1080/400) = 2.25 缩放视频帧(保持原始宽高比),以获取 1440x1080 的中间视频帧。
  2. 将 1440x1080 的视频帧与 1920x1080 PreviewView 对齐。
    • 对于 FIT_CENTER,将视频帧与 PreviewView 窗口的中心对齐。PreviewView 起始位置和结束位置的 240 像素列是空白的。
    • 对于 FIT_START,将视频帧与 PreviewView 窗口的起始位置(左上角)对齐。PreviewView 结束位置的 480 像素列是空白的。
    • 对于 FIT_END,将视频帧与 PreviewView 窗口的结束位置(右下角)对齐。PreviewView 起始位置的 480 像素列是空白的。

下图显示了 FILL_START / FILL_CENTER / FILL_END 缩放过程:

显示 FILL_START、FILL_CENTER 和 FILL_END 缩放过程的图片

该工作流程如下:

  1. 使用 max(1920/640, 1080/400) = 3 缩放视频帧,以获取 1920x1440(尺寸大于 PreviewView)的中间视频帧。
  2. 裁剪 1920x1440 的视频帧,使其适应 1920x1080 的 PreviewView 窗口。
    • 对于 FILL_CENTER,从缩放版 1920x1440 视频的中心开始剪裁出 1920x1080 部分。视频顶部和底部的 180 行均不会显示。
    • 对于 FILL_START,从缩放版 1920x1440 视频的起始位置开始剪裁出 1920x1080 部分。视频底部的 360 行不会显示。
    • 对于 FILL_END,从缩放版 1920x1440 视频的结束位置剪裁出 1920x1080 部分。视频顶部的 360 行不会显示。

其他资源

如需详细了解 CameraX,请参阅下面列出的其他资源。

Codelab

  • CameraX 使用入门
  • 代码示例

  • CameraX 示例应用