在向应用添加预览时,请使用 PreviewView
,这是一种可以剪裁、缩放和旋转以确保正确显示的 View
。
当相机处于活动状态时,图片预览会流式传输到 PreviewView
中的 Surface。
使用 PreviewView
如需使用 PreviewView
实现 CameraX 预览,请按以下步骤操作(稍后将对这些步骤进行说明):
- (可选)配置
CameraXConfig.Provider
。 - 将
PreviewView
添加到布局。 - 请求
ProcessCameraProvider
。 - 在创建
View
时,请检查ProcessCameraProvider
。 - 选择相机并绑定生命周期和用例。
使用 PreviewView
存在一些限制。使用 PreviewView
时,您无法执行以下任何操作:
- 创建
SurfaceTexture
,以在TextureView
和Preview.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
后,请执行以下操作:
- 创建
Preview
。 - 指定所需的相机
LensFacing
选项。 - 将所选相机和任意用例绑定到生命周期。
- 将
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
显示视频串流,但在某些情况下会回退为使用TextureView
。SurfaceView
具有专用的绘图界面,该对象更有可能通过内部硬件合成器实现硬件叠加层,尤其是当预览视频上面没有其他界面元素(如按钮)时。通过使用硬件叠加层进行渲染,视频帧会避开 GPU 路径,从而能降低平台功耗并缩短延迟时间。COMPATIBLE
模式。在此模式下,PreviewView
会使用TextureView
;不同于SurfaceView
,该对象没有专用的绘图表面。因此,视频要通过混合渲染,才能显示。在这个额外的步骤中,应用可以执行额外的处理工作,例如不受限制地缩放和旋转视频。
您可以使用 PreviewView.setImplementationMode()
选择适合具体应用的实现模式。如果默认的 PERFORMANCE
模式不适合您的应用,请参阅以下代码示例,了解如何设置 COMPATIBLE
模式:
Kotlin
// viewFinder is a PreviewView instance viewFinder.implementationMode = PreviewView.ImplementationMode.COMPATIBLE
缩放类型
当预览视频分辨率与目标 PreviewView
的尺寸不同时,视频内容需要通过剪裁操作或添加遮幅式黑边来适应视图(保持原始宽高比)。为此,PreviewView
提供了以下 ScaleTypes
:
FIT_CENTER
、FIT_START
和FIT_END
,用于添加遮幅式黑边。整个视频内容会调整(放大或缩小)为可在目标PreviewView
中显示的最大尺寸。不过,虽然整个视频帧会完整显示,但屏幕画面中可能会出现空白部分。视频帧会与目标视图的中心、起始或结束位置对齐,具体取决于您在上述三种缩放类型中选择了哪一种。FILL_CENTER
、FILL_START
和FILL_END
,用于进行剪裁。如果视频的宽高比与PreviewView
不匹配,画面中只会显示部分内容,但视频仍会填满整个PreviewView
。
CameraX 使用的默认缩放类型是 FILL_CENTER
。您可以使用 PreviewView.setScaleType()
设置最适合具体应用的缩放类型。下面的代码示例设置了 FIT_CENTER
缩放类型:
Kotlin
// viewFinder is a PreviewView instance viewFinder.scaleType = PreviewView.ScaleType.FIT_CENTER
视频显示过程包括以下步骤:
- 缩放视频:
- 如果您选择的缩放类型是
FIT_*
,请使用min(dst.width/src.width, dst.height/src.height)
缩放视频。 - 如果您选择的缩放类型是
FILL_*
,请使用max(dst.width/src.width, dst.height/src.height)
缩放视频。
- 如果您选择的缩放类型是
- 将经过缩放的视频与目标
PreviewView
对齐:- 对于
FIT_CENTER/FILL_CENTER
,请将经过缩放的视频与目标PreviewView
居中对齐。 - 对于
FIT_START/FILL_START
,请以每个视频的左上角为准,将经过缩放的视频与目标PreviewView
对齐。 - 对于
FIT_END/FILL_END
,请以每个视频的右下角为准,将经过缩放的视频与目标PreviewView
对齐。
- 对于
例如,下面是 640x480 的源视频和 1920x1080 的目标 PreviewView
:
下图显示了 FIT_START
/ FIT_CENTER
/ FIT_END
缩放过程:
该工作流程如下:
- 使用
min(1920/640, 1080/480) = 2.25
缩放视频帧(保持原始宽高比),以获取 1440x1080 的中间视频帧。 - 将 1440x1080 的视频帧与 1920x1080
PreviewView
对齐。- 对于
FIT_CENTER
,将视频帧与PreviewView
窗口的中心对齐。PreviewView
起始位置和结束位置的 240 像素列是空白的。 - 对于
FIT_START
,将视频帧与PreviewView
窗口的起始位置(左上角)对齐。PreviewView
结束位置的 480 像素列是空白的。 - 对于
FIT_END
,将视频帧与PreviewView
窗口的结束位置(右下角)对齐。PreviewView
起始位置的 480 像素列是空白的。
- 对于
下图显示了 FILL_START
/ FILL_CENTER
/ FILL_END
缩放过程:
该工作流程如下:
- 使用
max(1920/640, 1080/480) = 3
缩放视频帧,以获取 1920x1440(尺寸大于PreviewView
)的中间视频帧。 - 裁剪 1920x1440 的视频帧,使其适应 1920x1080 的
PreviewView
窗口。- 对于
FILL_CENTER
,从缩放版 1920x1440 视频的中心开始剪裁出 1920x1080 部分。视频顶部和底部的 180 行均不会显示。 - 对于
FILL_START
,从缩放版 1920x1440 视频的起始位置开始剪裁出 1920x1080 部分。视频底部的 360 行不会显示。 - 对于
FILL_END
,从缩放版 1920x1440 视频的结束位置剪裁出 1920x1080 部分。视频顶部的 360 行不会显示。
- 对于
其他资源
如需详细了解 CameraX,请参阅下面列出的其他资源。
Codelab
代码示例