Примечание. На этой странице рассматривается пакет Camera2. Если вашему приложению не требуются определенные низкоуровневые функции Camera2, рекомендуем использовать CameraX. Обе библиотеки поддерживают 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 описывает направление камеры относительно экрана устройства и может принимать одно из следующих значений:
CameraMetadata.LENS_FACING_FRONTCameraMetadata.LENS_FACING_BACKCameraMetadata.LENS_FACING_EXTERNAL
Дополнительную информацию о конфигурации lens-facing можно найти в статье 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 } }
Как разрешить переключение камер
Во многих приложениях для камеры пользователи могут переключаться между камерами.
У многих устройств есть несколько камер, направленных в одну сторону. В некоторых даже есть внешние 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.
В целом, используйте API Camera.getCameraInfo(), чтобы запрашивать все orientation камеры, и показывайте только одну камеру для каждой доступной ориентации пользователям, которые переключаются между камерами.
Поддержка всех типов устройств
Не думайте, что ваше приложение всегда будет работать на мобильном устройстве с одной или двумя камерами. Вместо этого выберите наиболее подходящие камеры для приложения. Если вам не нужна конкретная камера, выберите первую камеру, направленную в выбранном направлении. Если камера, направленная в определенную сторону, недоступна, подумайте, может ли пользователь завершить свой путь с другой камерой. Не ограничивайте доступность приложения на определенных устройствах в зависимости от аппаратного обеспечения камеры. Если подключена внешняя камера, пользователь, скорее всего, предпочитает использовать ее по умолчанию.