เลนส์กล้องและความสามารถของกล้อง

หมายเหตุ: หน้านี้เกี่ยวข้องกับแพ็กเกจ camera2 เราขอแนะนำให้ใช้ cameraX เว้นแต่ว่าแอปของคุณต้องใช้ฟีเจอร์ระดับต่ำที่เฉพาะเจาะจงจาก Camera2 ทั้ง CameraX และ Camera2 รองรับ Android 5.0 (API ระดับ 21) ขึ้นไป

อุปกรณ์ Android สมัยใหม่จำนวนมากมีกล้อง 2 ตัวขึ้นไปที่ด้านหน้า ด้านหลัง หรือ ทั้ง 2 ด้านของอุปกรณ์ เลนส์แต่ละตัวมีความสามารถเฉพาะตัว เช่น การจับภาพต่อเนื่อง การควบคุมด้วยตนเอง หรือการติดตามการเคลื่อนไหว แอปสำหรับการฝากเช็คอาจใช้ เป็นกล้องหลังตัวแรก ในขณะที่แอปโซเชียลมีเดียอาจเป็นค่าเริ่มต้น กล้องหน้า แต่ให้ตัวเลือกแก่ผู้ใช้ในการสลับใช้งาน เลนส์ และจดจำตัวเลือกของผู้ใช้ได้

หน้านี้จะพูดถึงวิธีแสดงรายการเลนส์กล้องและความสามารถเพื่อให้คุณ ทำให้ตัดสินใจภายในแอปเกี่ยวกับเลนส์ที่จะใช้ในสถานการณ์ต่างๆ ข้อมูลโค้ดต่อไปนี้จะดึงรายการกล้องทั้งหมดและทำซ้ำ ดังนี้

KotlinJava
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) }
   
...
}
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 อธิบายทิศทางที่กล้องหันไป สัมพันธ์กับหน้าจออุปกรณ์ และมีค่าใดค่าหนึ่งต่อไปนี้

หากต้องการข้อมูลเพิ่มเติมเกี่ยวกับการกำหนดค่าแบบเลนส์หน้าตรง โปรดดู CameraCharacteristics.LENS_FACING

ตัวแปร cameraCapabilities จากตัวอย่างโค้ดก่อนหน้านี้ประกอบด้วย ข้อมูลความสามารถอื่นๆ รวมถึงบอกว่ากล้อง สามารถสร้างเฟรมมาตรฐานเป็นเอาต์พุตได้ (ตัวอย่างเช่น เฉพาะ ข้อมูลเซ็นเซอร์ความลึก) คุณสามารถดูว่า CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE เป็นหนึ่งในความสามารถที่กล้องแสดงไว้ ซึ่งจะได้รับการจัดเก็บเป็นแฟล็กใน isBackwardCompatible

เลือกค่าเริ่มต้นที่เหมาะสม

คุณอาจต้องการเปิดกล้องบางตัวโดยค่าเริ่มต้นในแอป (หากมี) เช่น แอปเซลฟีอาจเปิดหน้าขึ้นมา ในขณะที่แอป Augmented Reality อาจเริ่มต้นด้วยกล้องหลัง ฟังก์ชันต่อไปนี้จะแสดงกล้องตัวแรกที่หันไปทางทิศทางที่กำหนด

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

เปิดใช้การสลับกล้อง

แอปกล้องหลายแอปมีตัวเลือกให้ผู้ใช้สลับกล้องได้ ดังนี้

วันที่
รูปที่ 1 ปุ่มเปลี่ยนกล้องในแอป Google กล้องถ่ายรูป

อุปกรณ์จำนวนมากมีกล้องหลายตัวที่หันหน้าไปในทิศทางเดียวกัน บางคนถึงขั้น กล้อง USB ภายนอก เพื่อให้ผู้ใช้มี UI ที่สามารถสลับใช้ กล้องแต่ละตัว ให้เลือกกล้องตัวแรกที่ใช้ได้ เลนส์

แม้ว่าจะไม่มีตรรกะสากลสำหรับการเลือกกล้องตัวถัดไป โค้ดต่อไปนี้ใช้ได้กับกรณีการใช้งานส่วนใหญ่

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

โค้ดนี้ใช้ได้กับอุปกรณ์จำนวนมากที่มี การกำหนดค่าเอง โปรดดูข้อมูลเพิ่มเติมเกี่ยวกับการพิจารณาเคส Edge Case ได้ที่ CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA

สร้างแอปที่เข้ากันได้

สำหรับแอปที่ยังใช้ Camera API ที่เลิกใช้งานแล้ว จำนวนกล้อง นั่น Camera.getNumberOfCameras() การแสดงผลจะขึ้นอยู่กับการใช้ OEM ถ้ามีกล้องหลายตัวเชิงตรรกะใน เพื่อรักษาความเข้ากันได้แบบย้อนหลังของแอป วิธีนี้จะแสดงเพียง สำหรับกล้องตรรกะและกลุ่มกล้องจริง ใช้ Camera2 API เพื่อดูกล้องทั้งหมด

สําหรับข้อมูลเพิ่มเติมเกี่ยวกับการวางแนวกล้อง โปรดดูที่ Camera.CameraInfo.orientation

โดยทั่วไป ให้ใช้เมธอด Camera.getCameraInfo() API สำหรับค้นหากล้องทั้งหมด orientation วินาที และแสดงกล้องเพียงตัวเดียวสำหรับแต่ละการวางแนวที่มีอยู่แก่ผู้ใช้ที่ สลับไปมาระหว่างกล้อง

รองรับอุปกรณ์ทุกประเภท

อย่าคิดเอาเองว่าแอปจะทำงานบนอุปกรณ์พกพาเสมอโดยใช้อุปกรณ์หรือ กล้อง 2 ตัว ให้เลือกกล้องที่เหมาะกับแอปมากที่สุดแทน หากคุณ หากไม่จำเป็นต้องใช้กล้องเฉพาะ ให้เลือกกล้องตัวแรกที่หันเข้าหาสิ่งที่ต้องการ เส้นทางการเรียนรู้ หากเชื่อมต่อกล้องภายนอก คุณอาจคิดว่าผู้ใช้ แนะนำให้ใช้เป็นค่าเริ่มต้น