Android适配多摄像头

时间:2024-04-02 19:39:03

从 Android P 开始,Android将添加对逻辑多摄像头和 USB 摄像头的支持。这意味着,除了前后两个摄像头外,Android手机的前置或者后置有两个及两个以上的摄像头。对此,对于Android开发者来说,就需要完成这方面的适配工作。

多摄像头

一台设备有多个摄像头没什么新鲜的,但是直到现在,Android 设备仍然最多只有前后两个摄像头。而新出的Android P系统将打破这一常规,今后Android将支持多个摄像头,用户想打开哪个就打开哪个。
在Android P出现之前,我们可以使用如下的一些方法来操作摄像头。

//判断是否存在摄像头
 fun hasCameraSupport():Boolean {
  return CONTEXT.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)
}
//获取摄像头个数
fun getNumberOfCameras():Int {
  if (Build.VERSION.SDK_INT >= 9)
  {
    return Camera.getNumberOfCameras()
  }
  else
  {
    return if (hasCameraSupport()) 1 else 0
  }
}

当然,如果想要打开某个摄像头,还可以使用下面的操作。例如,打开第一个摄像头:

val cameraDevice = Camera.open(0)

Camera2

由于兼容性问题,尽管旧的 Camera API 已经被废弃很长时间,上述的代码仍然有效。但是随着生态系统的发展,需要更先进的相机功能。因此,Android 5.0(Lollipop)引进了 Camera2,适用于 API 21 及以上。在Camera2中,我们可以使用下面的方式来打开摄像头。

val cameraManager = activity.getSystemService(Context.CAMERA_SERVICE) as CameraManager
val cameraId = cameraManager.cameraIdList[0]
cameraManager.openCamera(cameraId, object : CameraDevice.StateCallback() {
    override fun onOpened(device: CameraDevice) {
        // Do something with `device`
    }
    override fun onDisconnected(device: CameraDevice) {
        device.close()
    }
    override fun onError(device: CameraDevice, error: Int) {
        onDisconnected(device)
    }
}, null)

为了正确的打开某个摄像头,我们可以封装一个如下的函数来获取第一个摄像头。

fun getFirstCameraIdFacing(cameraManager: CameraManager,
                           facing: Int = CameraMetadata.LENS_FACING_BACK): String? {
    val cameraIds = cameraManager.cameraIdList
    cameraIds.forEach {
        val characteristics = cameraManager.getCameraCharacteristics(it)
        if (characteristics.get(CameraCharacteristics.LENS_FACING) == facing) {
            return it
        }
    }
    return cameraIds.firstOrNull()
}

当然,很多应用程序还为用户提供切换摄像头的功能,例如Google 相机应用中切换摄像头按钮。

Android适配多摄像头
当然,我们可以使用CameraManager.getCameraIdList()获取摄像头列表,然后选择下一个摄像头,但是这并不是个好的方式。因为从 Android P 开始,我们将会看到在同样的情况下更多的设备有多个摄像头,甚至有通过 USB 连接的外部摄像头。如果我们想要提供给用户切换不同摄像头的 UI,建议(按照文档)为每个可能的镜头配置选择第一个可用的摄像头。我们可以使用下面的代码来选择第一个可用的摄像头。

fun filterCameraIdsFacing(cameraIds: Array<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 = cameraManager.cameraIdList
    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 on 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)
    }
}

当然对于废弃的Camera,我们仍然可以使用 Camera.getNumberOfCameras()来获取摄像头的个数。具体的数量取决于 OEM 的具体实现。具体的适配工作可以参考Android P多摄像头API