Obiektywy i funkcje aparatu

Uwaga: ta strona dotyczy pakietu Aparat2. Jeśli Twoja aplikacja nie wymaga konkretnych, niskopoziomowych funkcji z Aparatu 2, zalecamy używanie AparatuX. Aparaty CameraX i Aparat 2 obsługują Androida 5.0 (poziom interfejsu API 21) i nowsze wersje.

Wiele nowoczesnych urządzeń z Androidem ma co najmniej dwa aparaty z przodu, z tyłu po obu stronach urządzenia. Każdy obiektyw ma unikalne funkcje, takie jak przechwytywanie serii, ręczne sterowanie lub śledzenie ruchu. Aplikacja do składania czeków może używać pierwszego tylnego aparatu, a aplikacja do mediów społecznościowych przednim aparatem, ale umożliwiać użytkownikom przełączanie się obiektywów. Zapamiętuje też wybrane przez Ciebie opcje.

Na tej stronie opisujemy, jak wyświetlić listę obiektywów i ich możliwości, aby może on podejmować w aplikacji decyzje o tym, którego obiektywu użyć w danej sytuacji. Ten fragment kodu pobiera listę wszystkich kamer i wykonuje iterację :

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 określa kierunek, w którym jest skierowany aparat względem ekranu urządzenia i ma jedną z tych wartości:

Więcej informacji o konfiguracji obiektywu: CameraCharacteristics.LENS_FACING

Zmienna cameraCapabilities z poprzedniego przykładowego kodu zawiera informacje o różnych możliwościach, w tym o tym, czy kamera mogą generować standardowe klatki jako dane wyjściowe (w przeciwieństwie do na przykład dane z czujnika głębi). Możesz sprawdzić, czy CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE jest jedną z funkcji kamery, która jest zapisywana jako flaga isBackwardCompatible

Wybierz rozsądne ustawienia domyślne

W swojej aplikacji prawdopodobnie chcesz domyślnie otwierać konkretną kamerę. (jeśli jest dostępna). Na przykład aplikacja do selfie prawdopodobnie otwiera przedni a aplikacja rzeczywistości rozszerzonej – od tylnego aparatu. Funkcja poniżej zwraca pierwszą kamerę, która jest skierowana w dany kierunek:

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 aparatów

Wiele aplikacji do obsługi aparatu umożliwia przełączanie się między aparatami:

Rysunek 1. Przycisk przełączania aparatu w aplikacji Aparat Google

Wiele urządzeń ma kilka kamer, które są skierowane w tym samym kierunku. Niektóre lub zewnętrznych aparatów USB. Udostępnienie użytkownikom interfejsu umożliwiającego przełączanie się między różnych aparatów, wybierz pierwszą dostępną kamerę dla każdego możliwego konfiguracji obiektywu.

Chociaż nie istnieje uniwersalna logika wyboru kolejnego aparatu, w większości przypadków sprawdza się 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;
}

Ten kod działa na dużej grupie urządzeń z wieloma różnymi konfiguracji. Więcej informacji na temat uwzględniania przypadków skrajnych znajdziesz w sekcji CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA.

Tworzenie zgodnych aplikacji

W przypadku aplikacji, które nadal korzystają z wycofanego interfejsu Camera API, liczba kamer które Camera.getNumberOfCameras() zwroty zależą od wdrożenia OEM. Jeśli w aplikacji znajduje się logiczny wieloma aparatami system, w celu zachowania zgodności wstecznej aplikacji, ta metoda spowoduje wyświetlenie tylko jednego dla każdej kamery logicznej i danej grupy aparatów fizycznych. Użyj interfejsu API Camera2, aby wyświetlić wszystkie kamery.

Więcej ogólnych informacji o orientacji aparatów znajdziesz na: Camera.CameraInfo.orientation

Ogólnie rzecz biorąc, Camera.getCameraInfo() Interfejs API do wysyłania zapytań dotyczących wszystkich kamer orientation, i udostępniać tylko jedną kamerę dla każdej dostępnej orientacji dla osób przełączanie się między aparatami.

Dla wszystkich typów urządzeń

Nie zakładaj, że Twoja aplikacja zawsze działa na urządzeniu przenośnym z dwóch kamer. Zamiast tego wybierz aparaty najlepiej dopasowane do aplikacji. Jeśli nie potrzebujesz konkretnej kamery, wybierz pierwszą kamerę, która jest skierowana w odpowiednią stronę. kierunek. Jeśli podłączona jest kamera zewnętrzna, możesz założyć, że użytkownik preferuje ten tryb jako domyślny.