注意:本页介绍的是 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) }
...
}
try {
String[] cameraIdList = cameraManager.getCameraIdList(); // may be empty
// iterate over available camera devices
for (String cameraId : cameraIdList) {
CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(cameraId);
int cameraLensFacing = characteristics.get(CameraCharacteristics.LENS_FACING);
int[] cameraCapabilities =
characteristics.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
// check if the selected camera device supports basic features
// ensures backward compatibility with the original Camera API
boolean isBackwardCompatible = false;
for (int capability : cameraCapabilities) {
if (capability == CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE) {
isBackwardCompatible = true;
break;
}
}
...
}
} catch (CameraAccessException e) {
Log.e(TAG, e.getMessage());
...
}
变量 cameraLensFacing
描述了镜头面向的方向
相对于设备屏幕,并具有以下值之一:
CameraMetadata.LENS_FACING_FRONT
CameraMetadata.LENS_FACING_BACK
CameraMetadata.LENS_FACING_EXTERNAL
如需详细了解面向镜头的配置,请参阅
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) }
}
}
public String getFirstCameraIdFacing(CameraManager cameraManager, @Nullable Integer facing) {
if (facing == null) facing = CameraMetadata.LENS_FACING_BACK;
String cameraId = null;
try {
// Get a list of all compatible cameras
String[] cameraIdList = cameraManager.getCameraIdList();
// Iterate over the list of cameras and return the first one matching desired
// lens-facing configuration and backward compatibility
for (String id : cameraIdList) {
CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(id);
int[] capabilities = characteristics.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
for (int capability : capabilities) {
if (capability == CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE
&& characteristics.get(CameraCharacteristics.LENS_FACING).equals(facing)) {
cameraId = id;
break;
}
}
}
// If no camera matches the desired orientation, return the first one from the list
cameraId = cameraIdList[0];
} catch (CameraAccessException e) {
Log.e(TAG, "getFirstCameraIdFacing: " + e.getMessage());
}
return cameraId;
}
允许切换摄像头
许多相机应用为用户提供切换摄像头的选项:

许多设备都有多个朝向同一方向的摄像头。有些人甚至还有 外接 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)
}
}
public List<String> filterCompatibleCameras(CameraManager cameraManager, String[] cameraIds) {
final List<String> compatibleCameras = new ArrayList<>();
try {
for (String id : cameraIds) {
CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(id);
int[] capabilities = characteristics.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
for (int capability : capabilities) {
if (capability == CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE) {
compatibleCameras.add(id);
}
}
}
} catch (CameraAccessException e) {
Log.e(TAG, "filterCompatibleCameras: " + e.getMessage());
}
return compatibleCameras;
}
public List<String> filterCameraIdsFacing(CameraManager cameraManager, List<String> cameraIds, int lensFacing) {
final List<String> compatibleCameras = new ArrayList<>();
try {
for (String id : cameraIds) {
CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(id);
if (characteristics.get(CameraCharacteristics.LENS_FACING) == lensFacing) {
compatibleCameras.add(id);
}
}
} catch (CameraAccessException e) {
Log.e(TAG, "filterCameraIdsFacing: " + e.getMessage());
}
return compatibleCameras;
}
public String getNextCameraId(CameraManager cameraManager, @Nullable String currentCameraId) {
String nextCameraId = null;
try {
// Get all front, back, and external cameras in 3 separate lists
List<String> compatibleCameraIds = filterCompatibleCameras(cameraManager, cameraManager.getCameraIdList());
List<String> backCameras = filterCameraIdsFacing(cameraManager, compatibleCameraIds, CameraMetadata.LENS_FACING_BACK);
List<String> frontCameras = filterCameraIdsFacing(cameraManager, compatibleCameraIds, CameraMetadata.LENS_FACING_FRONT);
List<String>externalCameras = filterCameraIdsFacing(cameraManager, compatibleCameraIds, CameraMetadata.LENS_FACING_EXTERNAL);
// The recommended order of iteration is: all external, first back, first front
List<String> allCameras = new ArrayList<>(externalCameras);
if (!backCameras.isEmpty()) allCameras.add(backCameras.get(0));
if (!frontCameras.isEmpty()) allCameras.add(frontCameras.get(0));
// Get the index of the currently selected camera in the list
int cameraIndex = allCameras.indexOf(currentCameraId);
// The selected camera may not be in the list, for example it could be an
// external camera that has been removed by the user
if (cameraIndex == -1) {
// Return the first camera from the list
nextCameraId = !allCameras.isEmpty() ? allCameras.get(0) : null;
else {
if (!allCameras.isEmpty()) {
// Return the next camera from the list, wrap around if necessary
nextCameraId = allCameras.get((cameraIndex + 1) % allCameras.size());
}
}
} catch (CameraAccessException e) {
Log.e(TAG, "getNextCameraId: " + e.getMessage());
}
return nextCameraId;
}
此代码适用于具有许多不同类型设备的大型
配置。如需详细了解如何考虑边缘情况,请参阅 CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA
。
创建兼容的应用
对于仍在使用已弃用的 Camera API 的应用,摄像头的数量
那个
Camera.getNumberOfCameras()
退货金额取决于 OEM 的实现情况。如果
为保持应用的向后兼容性,此方法仅公开
每个逻辑摄像头和底层物理摄像头组的摄像头。
使用 Camera2 API 查看所有摄像头。
有关摄像头方向的更多背景信息,请参阅
Camera.CameraInfo.orientation
。
一般来说,使用
Camera.getCameraInfo()
用于查询所有相机的 API
orientation
、
并针对每个可用的屏幕方向仅展示一个摄像头
来切换摄像头
适应所有设备类型
不要假设应用总是在具有一个或 两台摄像头而是应选择最适合该应用的摄像头。如果您 不需要特定摄像头,请选择第一个面向所需摄像头的 。如果连接了外接摄像头,您可能会认为用户 倾向于使用默认设置