ملاحظة: تشير هذه الصفحة إلى حزمة Camera2. ننصحك باستخدام الكاميراX ما لم يكن تطبيقك يتطلب ميزات محدَّدة منخفضة المستوى من تطبيق Camera2. يتوافق كل من CameraX و Camera2 مع نظام التشغيل Android 5.0 (المستوى 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()
والعائدات تعتمد على التنفيذ من قِبل المصنّع الأصلي للجهاز. فإذا كانت هناك كاميرات متعددة منطقية في
للحفاظ على التوافق مع الأنظمة القديمة للتطبيق، ستعرض هذه الطريقة واحدة فقط
لكل كاميرا منطقية ومجموعة كاميرات مادية أساسية.
استخدِم واجهة برمجة التطبيقات للكاميرا 2 للاطّلاع على جميع الكاميرات.
لمزيد من المعلومات حول اتجاهات الكاميرا، يمكنك الاطّلاع على
Camera.CameraInfo.orientation
بشكل عام، يمكنك استخدام العنصر
Camera.getCameraInfo()
واجهة برمجة التطبيقات لطلب البحث عن كل الكاميرات
orientation
،
وعرض كاميرا واحدة فقط لكل اتجاه متاح للمستخدمين الذين
والتبديل بين الكاميرات.
استيعاب جميع أنواع الأجهزة
لا تفترض أن تطبيقك يعمل دائمًا على جهاز محمول باليد كاميرتان. بدلاً من ذلك، اختر أنسب الكاميرات للتطبيق. إذا كنت لست بحاجة إلى كاميرا معيّنة، اختَر الكاميرا الأولى التي ستواجه الكاميرا المطلوبة. اتجاهك. في حالة توصيل كاميرا خارجية، فقد تفترض أن المستخدم ويفضلها كإعداد افتراضي.