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