支持多种外形规格的摄像头

Android 应用不仅能在竖屏模式下运行,还能在更多类型的设备上运行。随着桌面窗口化、连接的显示屏和可折叠设备的推出,相机应用必须适应动态窗口大小、不同的宽高比和外部硬件。

图 1. 不同显示屏上的相机应用示例。

手机逻辑中断的原因

相机应用经常会做出一些假设,导致在多形态规格设备环境中出现严重故障。

自然屏幕方向

  • 假设:设备的自然屏幕方向 ROTATION_0 始终为纵向
  • 实际情况:在平板电脑、部分可折叠设备的内屏和桌面显示器上,ROTATION_0 通常为横屏
  • 结果:预览错误地旋转了 90 度
图 2. 应用正确旋转之前的相机取景器和应用正确旋转之后的相机取景器。

传感器对齐

  • 假设:摄像头传感器的长边与屏幕的长边对齐
  • 实际情况:可调整大小的窗口可以是方形或横向,而传感器保持固定(通常为 4:3)
  • 结果:图片拉伸或出现其他失真
图 3. 应用正确的缩放比例因子前后的相机取景器。

屏幕密度和尺寸

  • 假设:屏幕的密度和大小在运行时不会发生变化
  • 事实:在桌面环境中,用户可以自由调整窗口大小
  • 结果:在每次拖动事件时重新启动相机工作时段会中断用户体验,并可能导致崩溃

解决方案 1:使用系统 intent

如果您的应用需要拍照或录制视频,但不需要专门的自定义相机界面,那么处理不同设备规格的最佳方式是启动设备预安装的系统相机(请参阅相机 intent

使用系统 intent 会将整个拍摄体验委托给设备原始设备制造商 (OEM) 开发的相机应用。这实际上是将外形规格支持的复杂性外包,包括:

  • 内置的大小调整和旋转支持 - 可折叠设备或平板电脑上的默认相机应用由制造商专门构建,用于处理相应设备的几何形状。应用在设备展开、旋转或置于多窗口模式时,能够正常运行。

  • 访问高级硬件功能 - OEM 相机应用可独占式访问经过硬件调优的算法(夜间模式、HDR、特定镜头切换),这些算法很难或无法手动复制。

解决方案 2:使用 Jetpack CameraX

CameraX 是一个 Jetpack 库,旨在帮助您更轻松地开发相机应用。CameraX 具有生命周期感知能力,并且面向 Surface。 与 Camera2 不同,后者在设备折叠、旋转或调整大小时需要手动重新计算传感器方向和表面尺寸,而 CameraX 在多窗口调整大小期间或应用移至连接的显示屏时会自动处理相机会话的重新配置,确保预览数据流能够顺畅地适应,不会出现卡顿或拉伸。

PreviewView 等组件可在不同状态(例如可折叠设备从外屏过渡到内屏)下智能管理宽高比和缩放类型,让您能够通过单个一致的实现来支持各种硬件,而不是通过复杂的设备特定边缘情况集合来实现。

撰写

使用 Jetpack Compose 时,请使用专用 androidx.camera:camera-compose 库。该库提供了 CameraXViewfinder 可组合函数,该函数专门用于在 Compose 生命周期内处理调整大小、旋转和宽高比的复杂几何形状。

CameraXViewfinder 组件可消除相机应用中最常见的错误来源:

  • 自动坐标转换 - 构建相机应用最难的部分之一是将用户的点按(屏幕上的 x、y 坐标)映射到相机传感器的坐标系(旋转后的 0-1、0-1),以实现对焦和测光。CameraXViewfinder 提供了一个 CoordinateTransformer,即使在窗口大小调整或设备折叠时,也能自动处理数学运算。
  • 正确的布局行为 - 与 SurfaceViewTextureView 不同,CameraXViewfinder 可与 Compose 的 z 顺序正确搭配使用。您可以叠加界面元素(焦点环、控件)或应用修饰符(圆角、动画),而不会出现渲染伪影。
  • 调整大小和宽高比CameraXViewfinder 在内部处理居中剪裁居中适应逻辑,确保当应用窗口调整为非标准宽高比(例如分屏或桌面窗口模式)时,预览不会拉伸。

View

在基于视图的应用中,使用 PreviewViewViewFinderView。 如果您直接使用 SurfaceViewTextureView,则必须自行计算宽高比并应用正确的转换矩阵

解决方案 3:动态处理屏幕方向和调整大小

直接使用平台 API 时,请注意设备旋转、activity 重启和宽高比。

停止使用设备旋转

请勿仅依赖 Display#getRotation() 或物理传感器方向来确定界面布局。

  • 使用窗口指标 - 通过使用 WindowManager#getCurrentWindowMetrics() 比较应用窗口的宽度和高度来确定布局(横屏与竖屏界面)。
  • 忽略自然屏幕方向 - 您的应用可能在横屏显示器上的纵向窗口中运行。设备屏幕方向与界面边界无关。

避免 activity 重启

默认情况下,Android 会在配置更改(例如调整窗口大小)时销毁应用的 activity。对于相机应用,这会表现为显示屏闪烁或视频通话期间连接断开。

  • 清单配置 - 在清单中声明配置变更,以便在不重新启动的情况下处理调整大小。
  • 动态更新 - 在 onConfigurationChanged() 中,更新相机预览的布局参数以匹配新的窗口大小。

宽高比和剪裁

可折叠设备和桌面窗口上常见的问题是预览拉伸,即强制将 4:3 的相机画面放入 16:9 或 1:1 的窗口中。

  • 不拉伸 - 如果预览和窗口的宽高比不同,则绝不强制使相机缓冲区与视图边界完全匹配。
  • 居中裁剪(推荐):缩放预览画面以填充窗口的最短维度,并裁剪多余部分。这样可确保拍摄对象不会变形,并填满整个画面。
  • 居中显示(替代方案):如果显示完整视野至关重要(例如扫描文档),则在窗口内以信箱模式显示预览。
  • 居中显示(替代方案):如果显示完整视野至关重要(例如扫描文档),则在窗口内以信箱模式显示预览。

奖励:支持以可折叠设备为先的体验

可折叠设备不仅仅是可弯曲的手机,它们还提供独特的硬件状态,可以从根本上改进用户拍摄照片和视频的方式。 与其将折叠视为需要解决的问题,不如利用它来构建在非折叠设备上无法实现的功能。

桌上模式(免触摸拍摄)

借助桌上模式,用户可以将设备半折叠并放在桌面上,以便进行长时间的视频通话、拍摄延时摄影照片和长曝光夜景照片。

图 5. 桌面模式下的通讯应用:相机取景器位于铰链顶部,控件位于底部。

后置显示屏模式(高品质自拍照)

  • 在可折叠设备上,后置摄像头的画质通常比面向用户的摄像头更高。 借助后置显示模式,用户可以展开设备并将其翻转过来,将小外屏用作主后置摄像头的实时取景器。
  • 后置显示屏模式可让您拍摄 5000 万像素以上的自拍照、超广角合照和高品质 Vlog,而无需携带额外的装备。

双屏模式(主题预览)

  • 借助双屏幕模式,您可以同时在内屏和外屏上显示相机预览。此功能非常适合拍摄人物照片:拍摄对象可以在外屏上看到自己,并调整姿势,而您可以在内屏上构图。
  • 与后屏模式(移动整个应用)不同,双屏模式会在外屏上创建一个辅助演示窗口。
图 5. 双屏模式下的相机应用。