实现预览

在向您的应用添加预览时,请使用 PreviewView,它是一个 View,可以裁剪、缩放和旋转以进行正确的显示。

当相机激活时,图像预览会流式传输到 PreviewView 内部的表面。

使用 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 具有专用的绘图表面,更有可能由 内部硬件合成器 使用硬件叠加层实现,尤其是在预览视频上方没有其他 UI 元素(如按钮)时。通过使用硬件叠加层进行渲染,视频帧可以避免 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 中显示的最大可能尺寸。但是,尽管整个视频帧可见,但屏幕的某些部分可能为空白。根据您选择的这三种缩放类型中的哪一种,视频帧将对齐到目标 View 的中心、开头或结尾。

  • 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

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

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

Image showing the FIT_START, FIT_CENTER, and FIT_END scaling process

该过程的工作原理如下

  1. 使用 min(1920/640, 1080/480) = 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 缩放过程

Image showing the FILL_START, FILL_CENTER, and FILL_END scaling process

该过程的工作原理如下

  1. 使用 max(1920/640, 1080/480) = 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 示例应用