相机镜头和功能

注意:本页介绍的是 Camera2 软件包。除非您的应用需要 Camera2 的特定底层功能,否则我们建议您使用 CameraX。CameraX 和 Camera2 都支持 Android 5.0(API 级别 21)及更高版本。

许多现代 Android 设备在正面、背面或两侧都有两个或更多摄像头。每个镜头都可以具有独特的功能,例如连拍、手动控制或动作追踪。用于存入支票的应用可能只会使用第一个后置摄像头,而社交媒体应用可能会默认使用前置摄像头,但会为用户提供在所有可用镜头之间切换的选项。它还可以记住用户的选择。

本页介绍了如何列出相机镜头及其功能,以便您在应用中决定在特定情况下使用哪个镜头。 以下代码段会检索所有摄像头的列表并对其进行迭代:

try {
    val cameraIdList = cameraManager.cameraIdList // may be empty

    // iterate over available camera devices.
    for (cameraId in cameraIdList) {
        val characteristics = cameraManager.getCameraCharacteristics(cameraId)
        val cameraLensFacing = characteristics.get(CameraCharacteristics.LENS_FACING)
        val cameraCapabilities = characteristics.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES)

        // check if the selected camera device supports basic features
        // ensures backward compatibility with the original Camera API.
        val isBackwardCompatible = cameraCapabilities?.contains(
            CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE
        ) ?: false
        // ...
    }
} catch (e: CameraAccessException) {
    e.message?.let { Log.e(TAG, it) }
    // ...
}

变量 cameraLensFacing 描述了摄像头相对于设备屏幕的朝向,并具有以下值之一:

如需详细了解镜头朝向配置,请参阅 CameraCharacteristics.LENS_FACING。

上述代码示例中的变量 cameraCapabilities 包含有关各种功能的信息,包括相机是否能够生成标准帧作为输出(而不是仅生成深度传感器数据)。您可以查看 CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE 是否是相机列出的功能之一,该功能在 isBackwardCompatible 中存储为标志。

选择合理的默认设置

在您的应用中,您可能希望默认打开特定摄像头(如果可用)。例如,自拍应用可能会打开前置摄像头,而增强现实应用可能会从后置摄像头开始。以下函数会返回朝向给定方向的第一个摄像头:

fun getFirstCameraIdFacing(
    cameraManager: CameraManager,
    facing: Int = CameraMetadata.LENS_FACING_BACK
): String? {
    try {
        // Get list of all compatible cameras.
        val cameraIds = cameraManager.cameraIdList.filter {
            val characteristics = cameraManager.getCameraCharacteristics(it)
            val capabilities = characteristics.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES)
            capabilities?.contains(
                CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE
            ) ?: false
        }

        // Iterate over the list of cameras and return the first one matching desired
        // lens-facing configuration.
        cameraIds.forEach {
            val characteristics = cameraManager.getCameraCharacteristics(it)
            if (characteristics.get(CameraCharacteristics.LENS_FACING) == facing) {
                return it
            }
        }

        // If no camera matched desired orientation, return the first one from the list.
        return cameraIds.firstOrNull()
    } catch (e: CameraAccessException) {
        e.message?.let { Log.e(TAG, it) }
        return null
    }
}

启用切换摄像头功能

许多相机应用都为用户提供了在不同摄像头之间切换的选项:

Google 相机应用界面,其中显示了切换摄像头按钮。
图 1. “Google 相机”应用中的“切换摄像头”按钮

许多设备都有多个朝向同一方向的摄像头。有些设备甚至配有外接 USB 摄像头。如需为用户提供一个界面,让他们能够在不同朝向的摄像头之间切换,请为每个可能的镜头朝向配置选择第一个可用的摄像头。

虽然没有用于选择下一个摄像头的通用逻辑,但以下代码适用于大多数用例:

fun filterCompatibleCameras(
    cameraIds: Array<String>,
    cameraManager: CameraManager
): List<String> {
    return cameraIds.filter {
        val characteristics = cameraManager.getCameraCharacteristics(it)
        characteristics.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES)?.contains(
            CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE
        ) ?: false
    }
}

fun filterCameraIdsFacing(
    cameraIds: List<String>,
    cameraManager: CameraManager,
    facing: Int
): List<String> {
    return cameraIds.filter {
        val characteristics = cameraManager.getCameraCharacteristics(it)
        characteristics.get(CameraCharacteristics.LENS_FACING) == facing
    }
}

fun getNextCameraId(cameraManager: CameraManager, currCameraId: String? = null): String? {
    // Get all front, back and external cameras in 3 separate lists.
    val cameraIds = filterCompatibleCameras(cameraManager.cameraIdList, cameraManager)
    val backCameras = filterCameraIdsFacing(
        cameraIds, cameraManager, CameraMetadata.LENS_FACING_BACK
    )
    val frontCameras = filterCameraIdsFacing(
        cameraIds, cameraManager, CameraMetadata.LENS_FACING_FRONT
    )
    val externalCameras = filterCameraIdsFacing(
        cameraIds, cameraManager, CameraMetadata.LENS_FACING_EXTERNAL
    )

    // The recommended order of iteration is: all external, first back, first front.
    val allCameras = (
        externalCameras + listOf(
            backCameras.firstOrNull(), frontCameras.firstOrNull()
        )
        ).filterNotNull()

    // Get the index of the currently selected camera in the list.
    val cameraIndex = allCameras.indexOf(currCameraId)

    // The selected camera may not be in the list, for example it could be an
    // external camera that has been removed by the user.
    return if (cameraIndex == -1) {
        // Return the first camera from the list.
        allCameras.getOrNull(0)
    } else {
        // Return the next camera from the list, wrap around if necessary.
        allCameras.getOrNull((cameraIndex + 1) % allCameras.size)
    }
}

此代码适用于具有许多不同配置的大量设备。如需详细了解如何考虑极端情况,请参阅 CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA。

创建兼容的应用

对于仍在使用已废弃的 Camera API 的应用,Camera.getNumberOfCameras() 返回的摄像头数量取决于 OEM 实现。如果系统中存在逻辑多摄像头,为了保持应用向后兼容性,此方法将仅针对每个逻辑摄像头和底层物理摄像头组公开一个摄像头。使用 Camera2 API 可查看所有摄像头。

如需详细了解相机屏幕方向,请参阅 Camera.CameraInfo.orientation。

通常,请使用 Camera.getCameraInfo() API 查询所有相机 orientation,并仅向在相机之间切换的用户公开每个可用方向的一个相机。

适应所有设备类型

不要假定应用始终在具有一个或两个摄像头的手持设备上运行。而是为应用选择最合适的摄像头。如果您不需要特定摄像头,请选择朝向所选方向的第一个摄像头。如果朝向特定方向的摄像头不可用,请考虑用户是否可以使用其他摄像头完成其历程。请勿根据相机硬件限制应用在某些设备上的可用性。 如果连接了外接摄像头,用户可能更倾向于将其作为默认摄像头。