实现预览

向应用添加预览时,请使用 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 具有专用的绘图 Surface,这更有可能由内部硬件合成器通过硬件叠加层实现,尤其是当预览视频上方没有其他 UI 元素(如按钮)时。通过硬件叠加层渲染,视频帧可以避免 GPU 路径,从而降低平台功耗和延迟。

  • COMPATIBLE 模式。在此模式下,PreviewView 使用 TextureView,它与 SurfaceView 不同,不具有专用的绘图 Surface。因此,视频会通过混合进行渲染,以便显示。在此额外步骤中,应用程序可以执行额外的处理,例如不受限制地缩放和旋转视频。

使用 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

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 示例应用