Kameraobjektive und Funktionen

Hinweis: Auf dieser Seite wird auf das Camera2-Paket verwiesen. Wenn Ihre App keine bestimmten Low-Level-Funktionen von Camera2 erfordert, empfehlen wir die Verwendung von CameraX. Sowohl CameraX als auch Camera2 unterstützen Android 5.0 (API-Level 21) und höher.

Viele moderne Android-Geräte haben zwei oder mehr Kameras auf der Vorder- und Rückseite oder auf beiden Seiten des Geräts. Jedes Objektiv kann einzigartige Funktionen haben, z. B. Serienaufnahmen, manuelle Steuerung oder Bewegungsverfolgung. Eine App zum Einzahlen von Schecks verwendet möglicherweise nur die erste nach hinten gerichtete Kamera, während eine Social-Media-App standardmäßig eine nach vorne gerichtete Kamera verwendet, Nutzern aber die Möglichkeit gibt, zwischen allen verfügbaren Objektiven zu wechseln. Die Auswahl kann auch gespeichert werden.

Auf dieser Seite wird beschrieben, wie Sie Kameraobjektive und ihre Funktionen auflisten, damit Sie in Ihrer App entscheiden können, welches Objektiv in einer bestimmten Situation verwendet werden soll. Das folgende Code-Snippet ruft eine Liste aller Kameras ab und durchläuft sie:

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());
    ...
}

Die Variable cameraLensFacing beschreibt die Richtung, in die die Kamera relativ zum Gerätebildschirm gerichtet ist, und hat einen der folgenden Werte:

Weitere Informationen zur Konfiguration der Objektivrichtung finden Sie unter CameraCharacteristics.LENS_FACING.

Die Variable cameraCapabilities aus dem vorherigen Codebeispiel enthält Informationen zu verschiedenen Funktionen, einschließlich der Frage, ob die Kamera Standardbilder als Ausgabe erzeugen kann (im Gegensatz zu z. B. nur Sensordaten des Tiefensensors). Sie können prüfen, ob CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE eine der aufgeführten Funktionen der Kamera ist, die als Flag in isBackwardCompatiblegespeichert ist.

Sinnvolle Standardeinstellungen auswählen

In Ihrer App möchten Sie wahrscheinlich standardmäßig eine bestimmte Kamera öffnen (falls verfügbar). Beispielsweise öffnet eine Selfie-App wahrscheinlich die nach vorne gerichtete Kamera, während eine Augmented-Reality-App mit der Rückkamera beginnt. Die folgende Funktion gibt die erste Kamera zurück, die in eine bestimmte Richtung gerichtet ist:

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;
}

Kamerawechsel aktivieren

Viele Kamera-Apps bieten Nutzern die Möglichkeit, zwischen Kameras zu wechseln:

Abbildung 1. Schaltfläche zum Wechseln der Kamera in der Google Kamera App

Viele Geräte haben mehrere Kameras, die in dieselbe Richtung gerichtet sind. Einige haben sogar externe USB-Kameras. Wenn Sie Nutzern eine Benutzeroberfläche zur Verfügung stellen möchten, mit der sie zwischen verschiedenen Kameras wechseln können, wählen Sie die erste verfügbare Kamera für jede mögliche Konfiguration der Objektivrichtung aus.

Es gibt zwar keine universelle Logik für die Auswahl der nächsten Kamera, aber der folgende Code funktioniert für die meisten Anwendungsfälle:

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;
}

Dieser Code funktioniert für eine Vielzahl von Geräten mit vielen verschiedenen Konfigurationen. Weitere Informationen zu Sonderfällen finden Sie unter CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA.

Kompatible Apps erstellen

Bei Apps, die noch die eingestellte Camera API verwenden, hängt die Anzahl der Kameras die Camera.getNumberOfCameras() zurückgegeben werden, von der OEM-Implementierung ab. Wenn im System eine logische Multikamera vorhanden ist, wird mit dieser Methode nur eine Kamera für jede logische Kamera und die zugrunde liegende Gruppe physischer Kameras verfügbar gemacht, um die Abwärtskompatibilität der App aufrechtzuerhalten. Verwenden Sie die Camera2 API, um alle Kameras zu sehen.

Weitere Hintergrundinformationen zu Kameraausrichtungen finden Sie unter Camera.CameraInfo.orientation.

Verwenden Sie im Allgemeinen die Camera.getCameraInfo() API, um alle orientations der Kamera abzufragen, und stellen Sie Nutzern, die zwischen Kameras wechseln, nur eine Kamera für jede verfügbare Ausrichtung zur Verfügung.

Alle Gerätetypen berücksichtigen

Gehen Sie nicht davon aus, dass Ihre App immer auf einem Mobilgerät mit einer oder zwei Kameras ausgeführt wird. Wählen Sie stattdessen die für die App am besten geeigneten Kameras aus. Wenn Sie keine bestimmte Kamera benötigen, wählen Sie die erste Kamera aus, die in die gewünschte Richtung gerichtet ist. Wenn die Kamera, die in eine bestimmte Richtung gerichtet ist, nicht verfügbar ist, überlegen Sie, ob der Nutzer seine Aufgabe mit einer anderen Kamera erledigen kann. Beschränken Sie die Verfügbarkeit Ihrer App auf bestimmten Geräten nicht aufgrund der Kamerahardware. Wenn eine externe Kamera angeschlossen ist, bevorzugt der Nutzer sie wahrscheinlich als Standardkamera.