ITPub博客

首页 > 移动开发 > ANDROID > Android 相机开发中的隐藏坑:如何正确设置预览和拍摄比例

Android 相机开发中的隐藏坑:如何正确设置预览和拍摄比例

原创 ANDROID 作者:山有木xi 时间:2024-02-26 11:38:57 0 删除 编辑

在 Android 相机开发中,「预览比例」和「拍摄比例」的设置是一个绕不过去的问题。我们在日常开发中可能常常看到 3:4、9:16、1:1,甚至是所谓的“全屏比例”被设置为预览或拍摄尺寸。但这些比例的背后,其实隐藏着一个容易忽略、却又很容易踩坑的关键点: 相机支持的尺寸和屏幕尺寸不是一一对应的,尤其是在全面屏设备上。

 

一、常见比例与“全屏”的误区

我们先回顾一下开发中经常用到的比例:

  • 3:4(竖拍常用)

  • 9:16(传统全屏比例)

  • 1:1(方形拍摄)

  • 全屏(根据设备屏幕尺寸计算得出)

在很多项目中,为了实现所谓的“全屏拍照”,开发者往往会直接使用设备屏幕的比例来设置相机预览和拍摄尺寸。这种方式在大部分设备上确实能正常运行,但在某些全面屏设备上,却会出现 黑屏、 崩溃、 无法拍摄等问题。

为什么?

二、问题根源:屏幕比例 vs 相机支持比例

全面屏时代之后,各大厂商为了追求屏占比,推出了越来越奇特的屏幕比例,常见的如:

  • 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
}

这段代码从本质上避免了“拍着拍着黑屏”的问题。与其强求设备支持我们需要的分辨率,不如 从设备支持的尺寸中找到最合适的一个。

四、更进一步的优化建议

1. 按比例选取,而非像素尺寸

上面的方法可以运行,但在有些分辨率较大的设备上差异仍然会很大,更推荐的是计算目标屏幕比例,再选出 最接近该比例的 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
}

2. 区分预览比例与拍照比例

注意:预览比例与最终拍照输出比例 不一定要完全一致。

可以设置一个尽量接近屏幕比例的预览尺寸,提高用户体验;而拍照尺寸可以单独选择更高分辨率、或更标准的宽高比,比如 4:3、16:9,以方便裁剪或上传。

五、总结

相机预览与拍照比例的设置,不能简单等于屏幕尺寸。

  • 全屏≠相机支持的比例,尤其是在全面屏设备上

  • 尽量从相机支持的尺寸中选择最接近的,而不是强行设定

  • 推荐通过 比例匹配来选择合适的 Size,而不是直接对比像素值

  • 避免黑屏和分辨率异常的关键在于: 尊重相机硬件的能力


来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/69917874/viewspace-3007313/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论
Oracle OCA(Java),IBM高级工程师认证,中国软件行业人才(高级c语言),高级Android工程师,对数据库并发与性能调优也有一定了解

注册时间:2019-04-25

  • 博文量
    147
  • 访问量
    390764