Kamera objektifleri ve özellikleri

Not: Bu sayfada Camera2 paketi ele alınmaktadır. Uygulamanız Camera2'den belirli, düşük seviyeli özellikler gerektirmiyorsa CameraX'i kullanmanızı öneririz. Hem CameraX hem de Camera2, Android 5.0 (API düzeyi 21) ve sonraki sürümleri destekler.

Birçok modern Android cihazın ön, arka veya her iki tarafında iki ya da daha fazla kamera bulunur. Her lensin, seri çekim, manuel kontrol veya hareket İzleme gibi benzersiz özellikleri olabilir. Çek yatırma uygulaması yalnızca arkaya bakan ilk kamerayı kullanabilirken sosyal medya uygulaması varsayılan olarak öne bakan kamerayı kullanabilir ancak kullanıcılara mevcut tüm lensler arasında geçiş yapma seçeneği sunabilir. Ayrıca, kullanıcının tercihlerini de hatırlayabilir.

Bu sayfada, kamera lenslerini ve özelliklerini nasıl listeleyeceğiniz açıklanmaktadır. Böylece, uygulamanızda belirli bir durumda hangi lensin kullanılacağına karar verebilirsiniz. Aşağıdaki kod snippet'i tüm kameraların listesini alır ve bu kameralar üzerinde yinelenir:

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

cameraLensFacing değişkeni, kameranın cihaz ekranına göre baktığı yönü tanımlar ve aşağıdaki değerlerden birini alır:

Lense dönük yapılandırma hakkında daha fazla bilgi için CameraCharacteristics.LENS_FACING bölümüne bakın.

Önceki kod örneğindeki cameraCapabilities değişkeni, kameranın standart kareler oluşturup oluşturamayacağı (ör. yalnızca derinlik sensörü verileri yerine) gibi çeşitli özellikler hakkında bilgi içerir. CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE özelliğinin, kameranın listelenen özelliklerinden biri olup olmadığını kontrol edebilirsiniz. Bu özellik, isBackwardCompatible içinde bir işaret olarak saklanır.

Mantıklı varsayılanlar seçin

Uygulamanızda, varsayılan olarak belirli bir kamerayı açmak isteyebilirsiniz (varsa). Örneğin, bir selfie uygulaması muhtemelen ön kamerayı açarken bir artırılmış gerçeklik uygulaması arka kamerayla başlayabilir. Aşağıdaki işlev, belirli bir yöne bakan ilk kamerayı döndürür:

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

Kameralar arasında geçişi etkinleştirme

Birçok kamera uygulaması, kullanıcılara kameralar arasında geçiş yapma seçeneği sunar:

Şekil 1. Google Kamera uygulamasındaki kamera değiştirme düğmesi

Birçok cihazda aynı yöne bakan birden fazla kamera bulunur. Hatta bazıları harici USB kameralara da sahiptir. Kullanıcılara farklı yönlere bakan kameralar arasında geçiş yapmalarını sağlayan bir kullanıcı arayüzü sunmak için olası her lens yönü yapılandırması için mevcut ilk kamerayı seçin.

Bir sonraki kamerayı seçmek için evrensel bir mantık olmasa da aşağıdaki kod çoğu kullanım alanında çalışır:

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

Bu kod, birçok farklı yapılandırmaya sahip çok sayıda cihazda çalışır. Sıra dışı durumları hesaba katma hakkında daha fazla bilgi için CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA başlıklı makaleyi inceleyin.

Uyumlu uygulamalar oluşturma

Hâlâ kullanımdan kaldırılan Camera API'yi kullanan uygulamalarda, Camera.getNumberOfCameras() tarafından döndürülen kamera sayısı OEM uygulamasına bağlıdır. Sistemde mantıksal bir çoklu kamera varsa uygulamanın geriye dönük uyumluluğunu korumak için bu yöntem, her mantıksal kamera ve temel fiziksel kamera grubu için yalnızca bir kamera gösterir. Tüm kameraları görmek için Camera2 API'yi kullanın.

Kamera yönleri hakkında daha fazla bilgi için Camera.CameraInfo.orientation başlıklı makaleye bakın.

Genel olarak, tüm kamera Camera.getCameraInfo()'larına orientation sorgu göndermek ve kameralar arasında geçiş yapan kullanıcılara her kullanılabilir yön için yalnızca bir kamera göstermek üzere API'yi kullanın.

Tüm cihaz türlerine uyum sağlama

Uygulamanızın her zaman bir veya iki kameralı bir el cihazında çalışacağını varsaymayın. Bunun yerine, uygulama için en uygun kameraları seçin. Belirli bir kameraya ihtiyacınız yoksa istenen yöne bakan ilk kamerayı seçin. Belirli bir yöne bakan kamera kullanılamıyorsa kullanıcının yolculuğunu farklı bir kamerayla tamamlayıp tamamlayamayacağını değerlendirin. Uygulamanızın belirli cihazlarda kullanılabilirliğini kamera donanımına göre kısıtlamayın. Harici bir kamera bağlıysa kullanıcı muhtemelen bu kamerayı varsayılan olarak tercih ediyordur.