Lưu ý: Trang này đề cập đến gói Camera2. Bạn nên sử dụng CameraX, trừ phi ứng dụng yêu cầu các tính năng cụ thể ở cấp thấp trong Camera2. 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ó 2 camera trở lên ở trước, sau hoặc cả hai bên của thiết bị. Mỗi ống kính có thể có các tính năng riêng biệt, 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 chỉ có thể sử dụng máy ảnh mặt sau đầu tiên, trong khi ứng dụng mạng xã hội có thể mặc định sử dụng máy ảnh mặt trước, nhưng vẫn cho phép người dùng chuyển đổi giữa tất cả các ống kính có sẵn. Trợ lý cũng có thể ghi nhớ các lựa chọn của trẻ.
Trang này trình bày cách liệt kê ống kính máy ảnh và chức năng của ống kính đó để bạn có thể đưa ra quyết định về việc nên sử dụng ống kính nào trong một tình huống cụ thể trong ứng dụng. Đoạn mã sau đây truy xuất danh sách tất cả các máy ảnh và lặp lại qua các máy ảnh đó:
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 camera so với màn hình thiết bị, đồng thời 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 mặt ố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ả thông tin về việc máy ảnh có thể tạo khung tiêu chuẩn ở dạng đầu ra hay không (ví dụ: chỉ có dữ liệu cảm biến độ sâu). Bạn có thể tìm hiểu xem CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE
có phải là một trong các chức năng có trong danh sách của máy ảnh (được lưu trữ dưới dạng cờ trong isBackwardCompatible
) hay không.
Chọn chế độ mặc định hợp lý
Trong ứng dụng của mình, bạn nên mở một máy ảnh cụ thể theo mặc định (nếu có). Ví dụ: ứng dụng tự chụp ảnh chân dung có thể mở máy ảnh mặt trước, trong khi ứng dụng thực tế tăng cường có thể bắt đầu bằng máy ảnh sau. Hàm sau trả về máy ảnh đầu tiên đối diện với một hướng nhất định:
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 máy ảnh
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 camera hướng về cùng một hướng. Một số thiết bị thậm chí còn có camera USB ngoài. Để cung cấp cho người dùng giao diện người dùng cho phép họ chuyển đổi giữa các máy ảnh mặt trước, hãy chọn máy ảnh đầu tiên có sẵn cho mỗi cấu hình ống kính có thể có.
Mặc dù không có logic chung để chọn máy ảnh tiếp theo, nhưng đoạn mã sau đây phù hợp với 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 dùng được cho một nhóm lớn các thiết bị có nhiều cấu hình khác nhau. Để biết thêm thông tin về cách tính các trường hợp đặc biệt, hãy 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 dùng Camera API (API Máy ảnh) không dùng nữa, số lượng máy ảnh mà Camera.getNumberOfCameras()
trả về sẽ tuỳ thuộc vào phương thức triển khai của OEM. Nếu có nhiều máy ảnh logic trong hệ thống, thì để 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 máy ảnh cho mọi máy ảnh logic và nhóm máy ảnh thực cơ bản.
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
.
Nhìn chung, hãy sử dụng API Camera.getCameraInfo()
để truy vấn tất cả orientation
máy ảnh và chỉ hiển thị một máy ảnh cho mỗi hướng có sẵn cho người dùng đang chuyển đổi giữa các máy ảnh.
Dùng được 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 một thiết bị cầm tay có một hoặc hai máy ảnh. Thay vào đó, hãy chọn các máy ảnh phù hợp nhất cho ứ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 theo hướng bạn muốn. Nếu đã kết nối một máy ảnh bên ngoài, bạn có thể giả định rằng người dùng ưu tiên máy ảnh đó làm máy ảnh mặc định.