הערה: הדף הזה מתייחס לחבילה camera2. מומלץ להשתמש ב-CameraX, אלא אם לאפליקציה שלך נדרשים תכונות ספציפיות ברמה נמוכה. גם CameraX וגם Camera2 תומכים ב-Android 5.0 (רמת API 21) ואילך.
במכשירי Android מודרניים רבים יש שתי מצלמות או יותר בחזית, האחורית או משני הצדדים של המכשיר. לכל עדשה יכולות להיות יכולות ייחודיות, כמו צילום רציף, שליטה ידנית, או מעקב תנועה. אפליקציה להפקדת המחאות עשויה להשתמש רק במצלמה האחורית הראשונה, בעוד שאפליקציית מדיה חברתית עשויה ליצור כברירת מחדל מצלמה קדמית, אבל המשתמשים יכולים לעבור בין כל האפשרויות הזמינות עדשות מצלמה. הם גם יכולים לזכור את הבחירות שלהם.
בדף הזה נסביר איך לפרט עדשות מצלמה והיכולות שלהן, כדי יכול לקבל החלטות בתוך האפליקציה לגבי איזו עדשה להשתמש במצב נתון. קטע הקוד הבא מאחזר רשימה של כל המצלמות וחוזר על עצמו הם:
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
מתאר את הכיוון של המצלמה
ביחס למסך המכשיר, ויש לו אחד מהערכים הבאים:
CameraMetadata.LENS_FACING_FRONT
CameraMetadata.LENS_FACING_BACK
CameraMetadata.LENS_FACING_EXTERNAL
למידע נוסף על התצורה של עדשה, ראו
CameraCharacteristics.LENS_FACING
המשתנה cameraCapabilities
מדוגמת הקוד הקודמת מכיל
מידע על יכולות שונות, כולל אם המצלמה
שהוא יכול לייצר פריימים סטנדרטיים כפלט (בניגוד לפלט, למשל:
נתונים מחיישן העומק). אפשר לבדוק אם
CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE
הוא אחד מהיכולות הרשומות של המצלמה, והוא שמור כדגל
isBackwardCompatible
בחירת ברירות מחדל הגיוניות
באפליקציה שלך, סביר להניח שברצונך לפתוח מצלמה ספציפית כברירת מחדל (אם היא זמינה). לדוגמה, סביר להניח שאפליקציית סלפי פותחת את המצלמה הקדמית אבל אפליקציית מציאות רבודה עשויה להתחיל במצלמה האחורית. הפונקציה הבאה מחזירה את המצלמה הראשונה שפונה לכיוון נתון:
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; }
הפעלת מעבר בין מצלמות
באפליקציות מצלמה רבות, המשתמשים יכולים לעבור בין המצלמות:
במכשירים רבים יש כמה מצלמות שפונות לאותו כיוון. בחלקם יש אפילו מצלמות USB חיצוניות. כדי לספק למשתמשים ממשק משתמש שמאפשר להם לעבור מצלמות עם כיוונים שונים, צריך לבחור את המצלמה הראשונה שזמינה לכל אחת מהן תצורה של עדשה.
למרות שאין לוגיקה אוניברסלית לבחירת המצלמה הבאה, הקוד הבא פועל ברוב תרחישי השימוש:
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; }
הקוד הזה עובד עם קבוצה גדולה של מכשירים עם הרבה אפשרויות
הגדרות אישיות. למידע נוסף על הנהלת חשבונות במקרי קצה, ראו CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA
.
יצירת אפליקציות תואמות
באפליקציות שעדיין משתמשות ב- Camera API שהוצא משימוש, מספר המצלמות
ש
Camera.getNumberOfCameras()
ההחזרות תלויות בהטמעת ה-OEM. אם יש רשת מרובת מצלמות
כדי לשמור על תאימות לאחור של האפליקציה, השיטה הזו תחשוף רק
לכל מצלמה לוגית ולכל קבוצה של מצלמות פיזיות.
כדי לראות את כל המצלמות, צריך להשתמש ב- Camera2 API.
למידע נוסף על כיוון התצוגה של המצלמה:
Camera.CameraInfo.orientation
באופן כללי, צריך להשתמש
Camera.getCameraInfo()
API לשליחת שאילתה על כל המצלמות
orientation
שניות,
ולחשוף רק מצלמה אחת לכל כיוון זמין למשתמשים
לעבור בין מצלמות.
התאמה לכל סוגי המכשירים
אל תניח שהאפליקציה שלך תמיד פועלת במכשיר נייד עם שתי מצלמות. במקום זאת, בחרו את המצלמות שמתאימות לאפליקציה. אם אין צורך במצלמה ספציפית, בחר את המצלמה הראשונה שפונה לכיוון מסוים. אם מחוברת מצלמה חיצונית, אפשר להניח שהמשתמש מעדיפה אותו כברירת המחדל.