Uwaga: ta strona dotyczy pakietu Camera2. Jeśli aplikacja nie wymaga określonych, niskiego poziomu funkcji z Aparatu2, zalecamy użycie CameraX. Zarówno aplikacja CameraX, jak i Aparat 2 obsługują Androida 5.0 (poziom interfejsu API 21) i nowsze wersje.
Wiele nowoczesnych urządzeń z Androidem ma co najmniej dwie kamery z przodu, z tyłu lub po obu stronach. Każdy obiektyw może mieć unikalne funkcje, np. zdjęcia serii, sterowanie ręczne lub śledzenie ruchu. Aplikacja do realizowania czeków może korzystać tylko z pierwszego tylnego aparatu, podczas gdy aplikacja mediów społecznościowych może domyślnie korzystać z przedniego aparatu, ale daje użytkownikom możliwość przełączania się między wszystkimi dostępnymi obiektywami. Może też pamiętać ich wybory.
Z tego artykułu dowiesz się, jak sporządzić listę obiektywów i ich możliwości, aby zdecydować, którego obiektywu użyć w danej sytuacji, w aplikacji. Ten fragment kodu pobiera listę wszystkich kamer i przetwarza je ponownie:
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()); ... }
Zmienna cameraLensFacing
opisuje kierunek, w którym kamera jest ustawiona, względem ekranu urządzenia, i ma jedną z tych wartości:
CameraMetadata.LENS_FACING_FRONT
CameraMetadata.LENS_FACING_BACK
CameraMetadata.LENS_FACING_EXTERNAL
Więcej informacji o konfiguracji obiektywu znajdziesz tutaj: CameraCharacteristics.LENS_FACING
.
Zmienna cameraCapabilities
z poprzedniego przykładowego kodu zawiera informacje o różnych możliwościach, m.in. o tym, czy aparat może generować standardowe klatki jako dane wyjściowe (w odróżnieniu od np. tylko danych z czujników głębi). Możesz sprawdzić, czy CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE
jest jedną z wymienionych funkcji kamery, która jest przechowywana jako flaga w isBackwardCompatible
.
Wybieranie rozsądnych ustawień domyślnych
W aplikacji możesz pewnie otwierać określony aparat (jeśli jest dostępny). Na przykład aplikacja do selfie zwykle otwiera przedni aparat, a aplikacja rzeczywistości rozszerzonej – tylny. Ta funkcja zwraca pierwszą kamerę, która jest skierowana w danym kierunku:
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; }
Włącz przełączanie kamer
Wiele aplikacji do obsługi aparatu umożliwia użytkownikom przełączanie się między aparatami:
Wiele urządzeń ma kilka kamer, które są skierowane w tym samym kierunku. Niektóre mają nawet zewnętrzne kamery USB. Aby zapewnić użytkownikom interfejs umożliwiający przełączanie się między różnymi aparatami, wybierz pierwszy dostępny aparat dla każdej możliwej konfiguracji obiektywu.
Chociaż nie ma uniwersalnej logiki wyboru kolejnej kamery, w większości przypadków działa ten kod:
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; }
Kod ten działa na wielu urządzeniach o wielu różnych konfiguracjach. Więcej informacji o uwzględnianiu przypadków skrajnych znajdziesz w artykule CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA
.
Tworzenie zgodnych aplikacji
W przypadku aplikacji, które nadal używają wycofanego interfejsu Camera API, liczba aparatów zwracanych przez Camera.getNumberOfCameras()
zależy od wdrożenia OEM. Jeśli system korzysta z logicznej, kilku kamer, aby zachować zgodność wsteczną aplikacji, ta metoda udostępnia tylko 1 kamerę dla każdej logicznej kamery i bazowej grupy kamer fizycznych.
Aby zobaczyć wszystkie kamery, użyj interfejsu Camera2 API.
Więcej informacji o orientacji aparatu znajdziesz na stronie Camera.CameraInfo.orientation
.
Ogólnie używaj interfejsu API Camera.getCameraInfo()
, aby wysyłać zapytania do wszystkich kamer orientation
i udostępniać tylko 1 kamerę na każdą dostępną orientację dla użytkowników przełączających się między kamerami.
Zgodność ze wszystkimi typami urządzeń
Nie zakładaj, że aplikacja zawsze działa na urządzeniu mobilnym z 1 lub 2 kamerami. Wybierz kamery, które najlepiej sprawdzą się w danej aplikacji. Jeśli nie potrzebujesz konkretnej kamery, wybierz pierwszą, która jest ustawiona w odpowiednim kierunku. Jeśli jest podłączona kamera zewnętrzna, możesz założyć, że użytkownik ustawi ją jako domyślną.