在 Android 相机开发中,「预览比例」和「拍摄比例」的设置是一个绕不过去的问题。我们在日常开发中可能常常看到 3:4、9:16、1:1,甚至是所谓的“全屏比例”被设置为预览或拍摄尺寸。但这些比例的背后,其实隐藏着一个容易忽略、却又很容易踩坑的关键点: 相机支持的尺寸和屏幕尺寸不是一一对应的,尤其是在全面屏设备上。
我们先回顾一下开发中经常用到的比例:
3:4(竖拍常用)
9:16(传统全屏比例)
1:1(方形拍摄)
全屏(根据设备屏幕尺寸计算得出)
在很多项目中,为了实现所谓的“全屏拍照”,开发者往往会直接使用设备屏幕的比例来设置相机预览和拍摄尺寸。这种方式在大部分设备上确实能正常运行,但在某些全面屏设备上,却会出现 黑屏、 崩溃、 无法拍摄等问题。
为什么?
全面屏时代之后,各大厂商为了追求屏占比,推出了越来越奇特的屏幕比例,常见的如:
18:9
19.5:9
20:9
21:9
甚至 22.5:9
而 Android 相机 API 中的预览和拍摄比例,其实是由硬件和驱动决定的,通常是比较传统的标准分辨率,比如:
1280×720(16:9)
1920×1080(16:9)
1440×1080(4:3)
1280×960(4:3)
这些尺寸是由
CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP 提供的,并
不会随着设备屏幕比例变化而变化。
在一个 2400×1080 的 20:9 屏幕上,若你尝试强行设置相机为该比例,系统可能找不到任何一个合适的分辨率去适配这个比例,就会导致:
黑屏(Surface 配置失败)
拍照失败(ImageReader 或 CameraCaptureSession 报错)
拍出奇怪比例的图片,例如 1920×1920(被强行裁切或拉伸)
我们来看一段推荐的设置方式:
private fun setupCamera() {
val cameraManager = getSystemService(Context.CAMERA_SERVICE) as CameraManager
try {
val cameraId = cameraManager.cameraIdList[0] // 获取相机ID
val characteristics = cameraManager.getCameraCharacteristics(cameraId)
val map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)
if (map == null) {
// 相机不支持预览
return
}
// 获取支持的预览尺寸列表
val sizes = map.getOutputSizes(SurfaceTexture::class.java)
// 选择最适合屏幕尺寸的预览尺寸
val bestSize = getBestPreviewSize(sizes, screenWidth, screenHeight)
// 设置预览尺寸
mPreviewSize = bestSize
} catch (e: CameraAccessException) {
e.printStackTrace()
}
}
private fun getBestPreviewSize(sizes: Array<Size>, screenWidth: Int, screenHeight: Int): Size {
// 选择最适合屏幕尺寸的预览尺寸
var bestSize = sizes[0]
var minDiff = Int.MAX_VALUE
for (size in sizes) {
val diff = Math.abs(size.width - screenWidth) + Math.abs(size.height - screenHeight)
if (diff < minDiff) {
minDiff = diff
bestSize = size
}
}
return bestSize
}
这段代码从本质上避免了“拍着拍着黑屏”的问题。与其强求设备支持我们需要的分辨率,不如 从设备支持的尺寸中找到最合适的一个。
上面的方法可以运行,但在有些分辨率较大的设备上差异仍然会很大,更推荐的是计算目标屏幕比例,再选出
最接近该比例的
Size,例如:
private fun getBestPreviewSizeByAspectRatio(
sizes: Array<Size>,
targetRatio: Float
): Size {
var bestSize = sizes[0]
var minRatioDiff = Float.MAX_VALUE
for (size in sizes) {
val ratio = size.width.toFloat() / size.height
val diff = abs(ratio - targetRatio)
if (diff < minRatioDiff) {
minRatioDiff = diff
bestSize = size
}
}
return bestSize
}
注意:预览比例与最终拍照输出比例 不一定要完全一致。
可以设置一个尽量接近屏幕比例的预览尺寸,提高用户体验;而拍照尺寸可以单独选择更高分辨率、或更标准的宽高比,比如 4:3、16:9,以方便裁剪或上传。
相机预览与拍照比例的设置,不能简单等于屏幕尺寸。
全屏≠相机支持的比例,尤其是在全面屏设备上
尽量从相机支持的尺寸中选择最接近的,而不是强行设定
推荐通过
比例匹配来选择合适的
Size,而不是直接对比像素值
避免黑屏和分辨率异常的关键在于: 尊重相机硬件的能力
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/69917874/viewspace-3007313/ ,如需转载,请注明出处,否则将追究法律责任。