Uwaga: ta strona dotyczy pakietu Camera2. Jeśli Twoja aplikacja nie wymaga konkretnych funkcji niskiego poziomu z Camera2, zalecamy używanie CameraX. Zarówno CameraX, jak i Camera2 obsługują Androida 5.0 (poziom API 21) i nowsze wersje.
Wiele nowoczesnych urządzeń z Androidem ma 2 lub więcej aparatów z przodu, z tyłu lub po obu stronach. Każdy obiektyw może mieć unikalne możliwości, takie jak robienie zdjęć seryjnych, ręczne sterowanie czy śledzenie ruchu. Aplikacja do wpłacania czeków może używać tylko pierwszego aparatu z tyłu, a aplikacja do mediów społecznościowych może domyślnie używać aparatu z przodu, ale umożliwiać użytkownikom przełączanie się między wszystkimi dostępnymi obiektywami. Może też zapamiętywać ich wybory.
Na tej stronie dowiesz się, jak wyświetlać listę obiektywów aparatu i ich możliwości, aby w aplikacji można było podejmować decyzje o tym, którego obiektywu użyć w danej sytuacji. Ten fragment kodu pobiera listę wszystkich aparatów i iteruje po niej:
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 aparat jest skierowany względem ekranu urządzenia, i ma jedną z tych wartości:
CameraMetadata.LENS_FACING_FRONTCameraMetadata.LENS_FACING_BACKCameraMetadata.LENS_FACING_EXTERNAL
Więcej informacji o konfiguracji kierunku obiektywu znajdziesz w artykule
CameraCharacteristics.LENS_FACING.
Zmienna cameraCapabilities z poprzedniego przykładu kodu zawiera informacje o różnych możliwościach, w tym o tym, czy aparat może generować standardowe klatki jako dane wyjściowe (w przeciwieństwie do np. danych z czujnika głębi). Możesz sprawdzić, czy
CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE
jest jedną z wymienionych możliwości aparatu, która jest przechowywana jako flaga w
isBackwardCompatible.
Wybieranie rozsądnych ustawień domyślnych
W aplikacji prawdopodobnie chcesz domyślnie otwierać określony aparat (jeśli jest dostępny). Na przykład aplikacja do selfie prawdopodobnie otworzy aparat z przodu, a aplikacja do rzeczywistości rozszerzonej może zacząć od aparatu z tyłu. Ta funkcja zwraca pierwszy aparat skierowany 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łączanie przełączania aparatów
Wiele aplikacji aparatu umożliwia użytkownikom przełączanie się między aparatami:
Wiele urządzeń ma kilka aparatów skierowanych w tym samym kierunku. Niektóre mają nawet zewnętrzne aparaty USB. Aby zapewnić użytkownikom interfejs, który umożliwia przełączanie się między różnymi aparatami, wybierz pierwszy dostępny aparat dla każdej możliwej konfiguracji kierunku obiektywu.
Chociaż nie ma uniwersalnej logiki wybierania następnego aparatu, ten kod działa w większości przypadków użycia:
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; }
Ten kod działa na wielu urządzeniach o różnych konfiguracjach. Więcej informacji o uwzględnianiu przypadków brzegowych znajdziesz w artykule CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA.
Tworzenie kompatybilnych aplikacji
W przypadku aplikacji, które nadal używają wycofanego interfejsu Camera API, liczba aparatów
zwracana
Camera.getNumberOfCameras()
zależy od implementacji OEM. Jeśli w systemie jest logiczny aparat wielokamerowy, aby zachować zgodność aplikacji z poprzednimi wersjami, ta metoda będzie udostępniać tylko 1 aparat dla każdego logicznego aparatu i grupy fizycznych aparatów.
Aby zobaczyć wszystkie aparaty, użyj interfejsu Camera2 API.
Więcej informacji o orientacji aparatu znajdziesz w artykule
Camera.CameraInfo.orientation.
Ogólnie rzecz biorąc, używaj interfejsu
Camera.getCameraInfo()
API, aby wysyłać zapytania o wszystkie
orientation aparatu,
i udostępniaj użytkownikom przełączającym się między aparatami tylko 1 aparat dla każdej dostępnej orientacji.
Obsługa wszystkich typów urządzeń
Nie zakładaj, że Twoja aplikacja zawsze będzie działać na urządzeniu przenośnym z 1 lub 2 aparatami. Zamiast tego wybierz aparaty najbardziej odpowiednie dla aplikacji. Jeśli nie potrzebujesz konkretnego aparatu, wybierz pierwszy aparat skierowany w żądanym kierunku. Jeśli aparat skierowany w określonym kierunku jest niedostępny, zastanów się, czy użytkownik może wykonać zadanie za pomocą innego aparatu. Nie ograniczaj dostępności aplikacji na niektórych urządzeniach na podstawie sprzętu aparatu. Jeśli podłączony jest aparat zewnętrzny, użytkownik prawdopodobnie woli go jako domyślny.