Lưu ý: Trang này đề cập đến gói Camera2. Trừ phi ứng dụng của bạn yêu cầu các tính năng cụ thể ở cấp độ thấp từ Camera2, bạn nên sử dụng CameraX. Cả CameraX và Camera2 đều hỗ trợ Android 5.0 (API cấp 21) trở lên.
Nhiều thiết bị Android hiện đại có từ 2 máy ảnh trở lên ở mặt trước, mặt sau hoặc cả hai bên của thiết bị. Mỗi ống kính có thể có các chức năng độc đáo, chẳng hạn như chụp liên tục, điều khiển thủ công hoặc theo dõi chuyển động. Ứng dụng để gửi séc có thể chỉ sử dụng máy ảnh mặt sau đầu tiên, trong khi một ứng dụng mạng xã hội có thể mặc định dùng máy ảnh mặt trước, nhưng cung cấp cho người dùng lựa chọn chuyển đổi giữa tất cả ống kính. Ứng dụng này cũng có thể ghi nhớ những lựa chọn của trẻ.
Trang này trình bày cách liệt kê các ống kính camera và chức năng của chúng để bạn có thể đưa ra quyết định trong ứng dụng về việc sử dụng ống kính nào trong một tình huống nhất định. Đoạn mã sau đây truy xuất danh sách tất cả các máy ảnh và lặp lại chúng:
Kotlin
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) } ... }
Java
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()); ... }
Biến cameraLensFacing
mô tả hướng của máy ảnh
tương ứng với màn hình thiết bị và có một trong các giá trị sau:
CameraMetadata.LENS_FACING_FRONT
CameraMetadata.LENS_FACING_BACK
CameraMetadata.LENS_FACING_EXTERNAL
Để biết thêm thông tin về cấu hình dành cho ống kính, hãy xem
CameraCharacteristics.LENS_FACING
.
Biến cameraCapabilities
trong mã mẫu trước đó chứa
thông tin về các tính năng khác, bao gồm cả việc camera có
có thể tạo kết quả các khung hình tiêu chuẩn (ví dụ: chỉ
dữ liệu cảm biến độ sâu). Bạn có thể tìm hiểu xem
CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE
là một trong những tính năng được liệt kê của camera và được lưu trữ dưới dạng cờ trong
isBackwardCompatible
.
Chọn các giá trị mặc định hợp lý
Trong ứng dụng của mình, có thể bạn sẽ muốn mở một máy ảnh cụ thể theo mặc định (nếu có). Ví dụ: một ứng dụng selfie có thể mở mặt trước còn ứng dụng thực tế tăng cường có thể khởi động bằng camera sau. Hàm sau đây trả về máy ảnh đầu tiên quay về một hướng cho trước:
Kotlin
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) } } }
Java
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; }
Bật tính năng chuyển đổi camera
Nhiều ứng dụng máy ảnh cho phép người dùng chuyển đổi giữa các máy ảnh:
Nhiều thiết bị có nhiều máy ảnh hướng theo cùng một hướng. Một số người thậm chí có camera USB bên ngoài. Để cung cấp cho người dùng một giao diện người dùng cho phép họ chuyển đổi giữa các máy ảnh mặt khác nhau, hãy chọn máy ảnh có sẵn đầu tiên cho mỗi máy ảnh có thể máy ảnh mặt trước.
Mặc dù không có logic chung để chọn camera tiếp theo, mã sau hoạt động cho hầu hết các trường hợp sử dụng:
Kotlin
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) } }
Java
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; }
Mã này hoạt động cho một tập hợp lớn các thiết bị có nhiều phiên bản
. Để biết thêm thông tin về cách tính đến các trường hợp hiếm gặp, vui lòng xem CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA
.
Tạo ứng dụng tương thích
Đối với các ứng dụng vẫn sử dụng API Máy ảnh không dùng nữa, số lượng máy ảnh
để
Camera.getNumberOfCameras()
lợi nhuận phụ thuộc vào cách triển khai của OEM. Nếu có một cụm camera logic trong
hệ thống của mình, để duy trì khả năng tương thích ngược của ứng dụng, phương thức này sẽ chỉ hiển thị một
camera cho mọi camera logic và nhóm camera thực bên dưới.
Sử dụng API Camera2 để xem tất cả các máy ảnh.
Để biết thêm thông tin cơ bản về hướng máy ảnh, hãy xem
Camera.CameraInfo.orientation
.
Nói chung, hãy sử dụng
Camera.getCameraInfo()
API để truy vấn tất cả máy ảnh
orientation
giây,
và chỉ phơi sáng một máy ảnh cho mỗi hướng có sẵn cho những người dùng
chuyển đổi giữa các camera.
Hỗ trợ mọi loại thiết bị
Đừng giả định rằng ứng dụng của bạn luôn chạy trên thiết bị cầm tay có một hoặc 2 camera. Thay vào đó, hãy chọn camera phù hợp nhất với ứng dụng. Nếu bạn không cần một máy ảnh cụ thể, hãy chọn máy ảnh đầu tiên hướng vào đối tượng mong muốn chỉ đường. Nếu máy ảnh bên ngoài được kết nối, bạn có thể cho rằng người dùng đặt nó làm mặc định.