معاينة الكاميرا

ملاحظة: تشير هذه الصفحة إلى حزمة Camera2. ننصحك باستخدام CameraX ما لم يكن تطبيقك يتطلّب ميزات معيّنة منخفضة المستوى من Camera2. يتوافق كل من CameraX و Camera2 مع نظام التشغيل Android 5.0 (المستوى 21 لواجهة برمجة التطبيقات) والإصدارات الأحدث.

لا يتم عرض الكاميرات ومعاينات الكاميرا دائمًا في الاتجاه نفسه على نظام التشغيل Android الأجهزة.

تكون الكاميرا في موضع ثابت على الجهاز، بغض النظر عمّا إذا كان الجهاز هي هاتف أو جهاز لوحي أو جهاز كمبيوتر. فعندما يتغير اتجاه الجهاز، تغييرات اتجاه الكاميرا.

ونتيجةً لذلك، تفترض تطبيقات الكاميرا عمومًا وجود علاقة ثابتة بين اتجاه الجهاز ونسبة العرض إلى الارتفاع لمعاينة الكاميرا. عندما الهاتف في الاتجاه العمودي، ومن المفترض أن تكون معاينة الكاميرا أطول. مما هو عريض. عند تدوير الهاتف (والكاميرا) إلى الوضع الأفقي، ينبغي أن تكون معاينة الكاميرا أعرض من طولها.

مع ذلك، تتحدى هذه الافتراضات أشكال أجهزة جديدة، مثل الأجهزة القابلة للطيّ. الأجهزة أو وأوضاع العرض مثل نوافذ متعددة أو العرض المتعدّد الهواتف القابلة للطي تُغيّر حجم العرض ونسبة العرض إلى الارتفاع بدون تغيير توجهك. يحصر وضع "النوافذ المتعددة" تطبيقات الكاميرا في جزء من الشاشة، ما يؤدي إلى تغيير حجم معاينة الكاميرا بغض النظر عن اتجاه الجهاز. يتيح وضع "العرض المتعدّد" استخدام شاشات ثانوية قد لا تكون بالاتجاه نفسه للشاشة الأساسية.

اتجاه الكاميرا

يحدِّد تعريف التوافق مع Android أنّه "يجب توجيه وحدة استشعار الصور في الكاميرا بحيث يتماشى البعد الطويل للكاميرا مع البعد الطويل للشاشة. أي، عندما إذا كان الجهاز في الاتجاه الأفقي، على الكاميرات التقاط الصور الاتجاه الأفقي. وينطبق ذلك بغض النظر عن اتجاه الجهاز الطبيعي، أي أنّه ينطبق على الأجهزة التي تستخدم الوضع الأفقي بشكل أساسي وعلى الأجهزة التي تستخدم الوضع العمودي بشكل أساسي.

يؤدي ترتيب الكاميرا إلى الشاشة إلى زيادة مساحة عرض الكاميرا عدسة الكاميرا في أحد تطبيقات الكاميرا. كما تعمل مستشعرات الصور عادةً على إخراج البيانات في ونسب العرض إلى الارتفاع الأفقية، حيث تكون 4:3 هي الأكثر شيوعًا.

يجب أن يكون كل من الهاتف ومستشعر الكاميرا في الوضع العمودي.
الشكل 1. العلاقة النموذجية بين اتجاه كاميرا الهاتف وجهاز الاستشعار

يكون الاتجاه الطبيعي لأداة الاستشعار في الكاميرا هو الوضع الأفقي. في الشكل 1، تحدد أداة الاستشعار الكاميرا الأمامية (تتوجه الكاميرا في نفس اتجاه الشاشة) يتم تدويرها بزاوية 270 درجة بالنسبة إلى الهاتف للتوافق مع تعريف التوافق مع Android

لعرض دوران أداة الاستشعار للتطبيقات، تتضمّن واجهة برمجة التطبيقات camera2 الثابت SENSOR_ORIENTATION . في معظم الهواتف والأجهزة اللوحية، يعرض الجهاز اتجاه أداة الاستشعار. 270 درجة للكاميرات الأمامية و90 درجة (نقطة الرؤية من الجزء الخلفي من الجهاز) للكاميرات الخلفية، والتي تعمل على محاذاة الحافة الطويلة مستشعرًا بالحافة الطويلة للجهاز. تُبلغ كاميرات الكمبيوتر المحمول بشكل عام عن اتجاه أداة الاستشعار 0 أو 180 درجة.

ولأنّ أدوات استشعار صور الكاميرا تُخرج بياناتها (مخزن مؤقت للصور) في الاتجاه الطبيعي لأداة الاستشعار (أفقية)، يجب تدوير المخزن المؤقت للصور عدد الدرجات التي يتم تحديدها من خلال SENSOR_ORIENTATION لمعاينة الكاميرا تظهر عموديًا في الاتجاه الطبيعي للجهاز. بالنسبة إلى الكاميرات الأمامية، تتم عملية الدوران بعكس عقارب الساعة، أما بالنسبة إلى الكاميرات الخلفية، فيتم الدوران باتجاه عقارب الساعة.

على سبيل المثال، بالنسبة إلى الكاميرا الأمامية في الشكل 1، يظهر مخزن الصور الذي ينتجه أداة استشعار الكاميرا على النحو التالي:

تم تدوير حساس الكاميرا إلى الوضع الأفقي مع ظهور الصورة
            بشكل جانبي في أعلى يمين الشاشة.

يجب تدوير الصورة 270 درجة عكس اتجاه عقارب الساعة لكي يتطابق اتجاه المعاينة مع اتجاه الجهاز:

أداة استشعار الكاميرا في الاتجاه العمودي مع صورة عمودية

ستنتج الكاميرا الخلفية مخزنًا مؤقتًا للصور بنفس الاتجاه المورد الاحتياطي أعلاه، ولكن تبلغ درجة حرارة SENSOR_ORIENTATION 90 درجة. ونتيجة لذلك، تم تدوير المخزن المؤقت 90 درجة في اتجاه عقارب الساعة.

تدوير الجهاز

يشير دوران الجهاز إلى عدد الدرجات التي يتم فيها تدوير الجهاز عن اتجاهه الطبيعي. على سبيل المثال، يكون تدوير الهاتف في الوضع الأفقي 90 أو 270 درجة، حسب اتجاه التدوير.

يجب تدوير المخزن المؤقت لصور أداة استشعار الكاميرا بنفس عدد الدرجات دوران الجهاز (بالإضافة إلى درجات اتجاه أداة الاستشعار) معاينة الكاميرا لتظهر في وضع مستقيم.

حساب الاتجاه

يأخذ الوضع الصحيح لمعاينة الكاميرا في الاعتبار أداة الاستشعار. الاتجاه وتدوير الجهاز.

يمكن احتساب إجمالي دوران مخزن صور أداة الاستشعار باستخدام العبارة التالية:

rotation = (sensorOrientationDegrees - deviceOrientationDegrees * sign + 360) % 360

حيث يكون sign هو 1 للكاميرات الأمامية و-1 للكاميرات الخلفية.

بالنسبة إلى الكاميرات الأمامية، يتم تدوير ذاكرة التخزين المؤقت للصور عكس اتجاه عقارب الساعة (من الاتجاه الطبيعي لجهاز الاستشعار). بالنسبة إلى الكاميرات الخلفية، يمكن استخدام أداة الاستشعار يتم تدوير المخزن المؤقت للصور في اتجاه عقارب الساعة.

يؤدّي التعبير deviceOrientationDegrees * sign + 360 إلى تحويل دوران الجهاز. من عكس عقارب الساعة إلى اتجاه عقارب الساعة للكاميرات الخلفية (على سبيل المثال، تحويل 270 درجة عكس اتجاه عقارب الساعة إلى 90 درجة باتجاه عقارب الساعة). باقي القسمة تعمل هذه العملية على تغيير النتيجة إلى أقل من 360 درجة (على سبيل المثال، التحجيم 540 درجة الدوران إلى 180).

تُبلِغ واجهات برمجة التطبيقات المختلفة عن تدوير الجهاز بشكل مختلف:

  • Display#getRotation() يعرض الجهاز عكس اتجاه عقارب الساعة (من وجهة نظر المستخدم). يتم إدخال هذه القيمة في الصيغة أعلاه كما هي.
  • OrientationEventListener#onOrientationChanged() يعرض هذا الرمز تدوير الجهاز باتجاه عقارب الساعة (من وجهة نظر المستخدم). نفي القيمة للاستخدام في الصيغة أعلاه.

الكاميرات الأمامية

معاينة الكاميرا وجهاز الاستشعار في الاتجاه الأفقي، أداة الاستشعار
            هي الجانب الأيمن إلى أعلى.
الشكل 2. معاينة الكاميرا وجهاز الاستشعار مع الهاتف الذي تم تدويره 90 درجة ليصبح في الوضع الأفقي

إليك المخزن المؤقت للصور الذي أنتجته أداة استشعار الكاميرا في الشكل 2:

أداة استشعار الكاميرا في الاتجاه الأفقي مع توجيه الصورة عموديًا

يجب تدوير المخزن المؤقت 270 درجة عكس عقارب الساعة لضبطه في أداة الاستشعار الاتجاه (راجِع اتجاه الكاميرا أعلاه):

تم تدوير أداة استشعار الكاميرا إلى اتجاه المنفذ مع صورة جانبية.
            أعلى اليمين.

بعد ذلك، يتم تدوير المخزن المؤقت 90 درجة أخرى عكس اتجاه عقارب الساعة للتمكّن من مراعاة تدوير الجهاز، ما يؤدي إلى ضبط الاتجاه الصحيح لمعاينة الكاميرا في الشكل 2:

تم تدوير أداة استشعار الكاميرا إلى الاتجاه الأفقي مع استخدام صورة
            مستقيمًا.

في ما يلي الكاميرا التي تم توجيهها إلى اليمين للوضع الأفقي:

تكون معاينة الكاميرا وجهاز الاستشعار في الاتجاه الأفقي، ولكن
            أداة الاستشعار مقلوبة.
الشكل 3. معاينة الكاميرا وأداة الاستشعار التي تعرض الهاتف بزاوية 270 درجة (أو -90 درجة) إلى الاتجاه الأفقي.

في ما يلي ذاكرة التخزين المؤقت للصور:

تم تدوير حساس الكاميرا إلى الوضع الأفقي مع قلب الصورة
            رأسًا على عقب.

يجب تدوير المخزن المؤقت 270 درجة عكس عقارب الساعة لضبطه في أداة الاستشعار الاتجاه:

تم تقييم أداة استشعار الكاميرا للاستخدام في الوضع العمودي مع وضع الصورة على الجانب،
            أعلى يمين الشاشة.

ثم يتم تدوير المخزن المؤقت 270 درجة أخرى عكس اتجاه عقارب الساعة لحساب دوران الجهاز:

تم تدوير أداة استشعار الكاميرا إلى الوضع الأفقي مع الصورة
            في الوضع العمودي.

الكاميرات الخلفية

عادةً ما يكون اتجاه الكاميرات الخلفية أداة الاستشعار 90 درجة (مثل يتم عرضها من الجزء الخلفي للجهاز). عند توجيه معاينة الكاميرا، يتم تدوير المخزن المؤقت لصور أداة الاستشعار في اتجاه عقارب الساعة من خلال مقدار دوران أداة الاستشعار (وليس عكس عقارب الساعة مثل الكاميرات الأمامية)، ثم يتم تدوير المخزن المؤقت عكس عقارب الساعة من خلال مقدار تدوير الجهاز.

تكون معاينة الكاميرا وجهاز الاستشعار في الاتجاه الأفقي، ولكن
            أداة الاستشعار مقلوبة.
الشكل 4. هاتف مزوّد بكاميرا خلفية في الوضع الأفقي (مُدار بزاوية 270 أو -90 درجة)

في الشكل 4، إليك وحدة تخزين مؤقت للصور من حساس الكاميرا:

تم تدوير حساس الكاميرا إلى الوضع الأفقي مع قلب الصورة
            رأسًا على عقب.

يجب تدوير المخفّض بزاوية 90 درجة في اتجاه عقارب الساعة لتعديل اتجاه الاستشعار:

أداة استشعار الكاميرا مصنَّفة على الاتجاه العمودي مع تقديم صورة جانبية
            أعلى اليمين.

ثم يتم تدوير المخزن المؤقت 270 درجة عكس عقارب الساعة لحساب الجهاز التدوير:

تم تدوير أداة استشعار الكاميرا إلى الوضع الأفقي مع الصورة
            في الوضع العمودي.

نسبة العرض إلى الارتفاع

تتغير نسبة العرض إلى الارتفاع للشاشة عند تغيير اتجاه الجهاز، ولكن أيضًا عند طيّ الأجهزة القابلة للطي وفتحها، وعند تغيير حجم النوافذ في البيئات التي تتضمّن نوافذ متعددة، وعند فتح التطبيقات على الشاشات الثانوية.

يجب توجيه ذاكرة التخزين المؤقت لصورة حساس الكاميرا وتغيير حجمها لتتطابق مع اتجاه ونسبة العرض إلى الارتفاع لعنصر واجهة مستخدِم عدسة الكاميرا عندما يغيّر واجهة المستخدِم الاتجاه ديناميكيًا، سواء كان الجهاز يغيّر الاتجاه أم لا.

في أشكال الأجهزة الجديدة أو في البيئات التي تتضمّن عدّة نوافذ أو عدّة شاشات، إذا افترض تطبيقك أنّ معاينة الكاميرا لها الاتجاه نفسه مثل الجهاز (عمودي أو أفقي)، قد يكون الاتجاه في المعاينة غير صحيح أو قد يكون التكبير/التصغير غير صحيح أو كليهما.

جهاز قابل للطي غير مطوي مع معاينة الكاميرا في الوضع العمودي بجانبه
الشكل 5. ينتقل الجهاز القابل للطي من نسبة العرض إلى الارتفاع العمودية إلى نسبة العرض إلى الارتفاع الأفقية، ولكن يظلّ مستشعر الكاميرا في الوضع العمودي.

في الشكل 5، افترض التطبيق عن طريق الخطأ أن الجهاز تم تدويره 90 درجات عكس عقارب الساعة؛ وهكذا، قام التطبيق بإجراء تدوير للمعاينة بنفس القدر.

جهاز قابل للطي غير مطوي مع معاينة الكاميرا في الوضع العمودي ولكن تم تصغيرها
            بسبب التوسيع غير الصحيح
الشكل 6. ينتقل الجهاز القابل للطي من نسبة العرض إلى الارتفاع في الوضع العمودي إلى الوضع الأفقي، ولكن يظلّ مستشعر الكاميرا في الوضع العمودي.

وفي الشكل 6، لم يضبط التطبيق نسبة العرض إلى الارتفاع للمخزن المؤقت للصور لتمكينها من التكيّف بشكل صحيح بحيث يتناسب مع الأبعاد الجديدة لواجهة مستخدم معاينة الكاميرا العنصر.

تواجه تطبيقات الكاميرا ذات الاتجاه الثابت عادةً مشاكل على الأجهزة القابلة للطي وغيرها من الأجهزة التي تتضمّن شاشة كبيرة، مثل أجهزة الكمبيوتر المحمول:

تظهر معاينة الكاميرا على الكمبيوتر المحمول بشكل رأسي، ولكن واجهة مستخدم التطبيق تظهر بشكل جانبي.
الشكل 7. تطبيق الوضع العمودي ذي الاتجاه الثابت على جهاز كمبيوتر محمول.

في الشكل 7، تظهر واجهة مستخدم تطبيق الكاميرا بشكل جانبي لأنّ اتجاه التطبيق محدود بالوضع العمودي فقط. تم توجيه صورة عدسة الكاميرا بشكل صحيح بالنسبة إلى أداة استشعار الكاميرا.

الوضع العمودي المُدمَج

يمكن وضع تطبيقات الكاميرا التي لا تتيح وضع "النوافذ المتعددة" (resizeableActivity="false") وتفرض اتجاهًا معيّنًا عليها (screenOrientation="portrait" أو screenOrientation="landscape") في وضع "صورة في صورة" على الأجهزة ذات الشاشات الكبيرة لضبط اتجاه معاينة الكاميرا بشكل صحيح.

عرض التطبيقات المخصّصة للوضع العمودي فقط في الوضع العمودي باستخدام إطارات مُدمجة في الوضع العمودي (المُدمَجة) حتى إذا كانت نسبة العرض إلى الارتفاع للشاشة أفقية يتم عرض التطبيقات باتجاه أفقي فقط في أشرطة أفقية في الاتجاه الأفقي على الرغم من تكون نسبة العرض إلى الارتفاع للعرض عموديًا. يتم تدوير صورة الكاميرا لمواءمتها مع واجهة مستخدم التطبيق، ويتم اقتصاصها لمطابقة نسبة العرض إلى الارتفاع لمعاينة الكاميرا، ثم تتم تسويتها لملء المعاينة.

يتم تفعيل وضع "بورتريه مضمّن" عندما لا تتطابق نسبة العرض إلى الارتفاع لحساس كاميرا الصورة ونسبة العرض إلى الارتفاع للنشاط الأساسي للتطبيق.

معاينة الكاميرا وواجهة مستخدم التطبيق في الاتجاه العمودي الصحيح على الكمبيوتر المحمول
            يتم تصغير صورة المعاينة على الشاشة العريضة واقتصاصها لتلائم الاتجاه عموديًا.
الشكل 8. تطبيق في الوضع العمودي الثابت في وضع عمودي مضمّن على الكمبيوتر المحمول

في الشكل 8، تم تدوير تطبيق الكاميرا للوضع العمودي فقط لعرض واجهة المستخدم في الوضع العمودي على شاشة الكمبيوتر المحمول. يتم عرض التطبيق بتنسيق شاشة عريضة أفقيًا بسبب الاختلاف في نسبة العرض إلى الارتفاع بين التطبيق بالوضع العمودي وشاشة العرض الأفقية. الكاميرا تم تدوير صورة المعاينة لتعويض تدوير واجهة مستخدم التطبيق (بسبب داخل وضع رأسي)، وتم اقتصاص الصورة وضبطها لتلائم الاتجاه الرأسي، ما يقلل من مجال الرؤية.

تدوير، اقتصاص، ضبط الحجم

يتم استدعاء وضع "بورتريه" الداخلي لتطبيق الكاميرا في وضع "بورتريه" فقط على الشاشة. بها نسبة عرض إلى ارتفاع أفقية:

تكون معاينة الكاميرا على الكمبيوتر المحمول في وضع عمودي ولكن واجهة مستخدم التطبيق تظهر بانحراف.
الشكل 9. تطبيق بالوضع العمودي الثابت على الكمبيوتر المحمول

يظهر التطبيق مُعدًّا للعرض على شاشة عريضة أفقيًا في الاتجاه العمودي:

تم تدوير التطبيق إلى الوضع العمودي وعرضه بتنسيق إطار مُعدّ للعرض على شاشة عريضة أفقيًا. الصورة هي
            إلى جانب الفيديو أو أعلى إلى اليمين.

يتم تدوير صورة الكاميرا بزاوية 90 درجة للتكيّف مع إعادة توجيه التطبيق:

تم تدوير صورة المستشعر بزاوية 90 درجة لتصبح في الوضع العمودي.

يتم اقتصاص الصورة لتصبح بنسبة العرض إلى الارتفاع نفسها في معاينة الكاميرا، ثم يتم تصغيرها لتلائم المساحة المخصّصة لمعاينة الصورة (يتم تقليل مجال الرؤية):

صورة الكاميرا التي تم اقتصاصها وتم تصغيرها لملء معاينة الكاميرا

في الأجهزة القابلة للطي، يمكن أن يكون اتجاه أداة استشعار الكاميرا عموديًا بينما تكون نسبة العرض إلى الارتفاع للشاشة أفقية:

معاينة الكاميرا وواجهة مستخدم التطبيق يتم تدويرهما إلى جانبَي شاشة عريضة غير مطوية
الشكل 10. الجهاز غير المطوي مع تطبيق كاميرا مخصّص للصور العمودية فقط ونسب عرض إلى ارتفاع مختلفة لحساس الكاميرا والشاشة

نظرًا لأنّه يتم تدوير معاينة الكاميرا لضبط اتجاه أداة الاستشعار، يتم ضبط اتجاه الصورة بشكل صحيح في عدسة الكاميرا، ولكن التطبيق المخصّص للصور العمودية فقط يكون بوضع أفقي.

لا يحتاج وضع "بورتريه" إلا إلى ضبط محتوى التطبيق على شاشة عريضة أفقيًا في الاتجاه العمودي. لتوجيه معاينة التطبيق والكاميرا بشكل صحيح:

تطبيق مُعدّ للعرض على شاشة عريضة أفقيًا بتنسيق إطار الصورة مع معاينة الكاميرا
            في وضع عمودي على الجهاز القابل للطي.

واجهة برمجة التطبيقات

اعتبارًا من Android 12 (المستوى 31 لواجهة برمجة التطبيقات)، يمكن للتطبيقات أيضًا التحكّم صراحةً في وضع الشاشة العمودية المضمّنة باستخدام السمة SCALER_ROTATE_AND_CROP في فئة CaptureRequest .

القيمة التلقائية هي SCALER_ROTATE_AND_CROP_AUTO، التي تتيح للنظام تفعيل الوضع العمودي المُدمَج. SCALER_ROTATE_AND_CROP_90 هو سلوك وضع رأسي الداخلي كما هو موضح أعلاه.

لا تتوافق بعض الأجهزة مع جميع قيَم SCALER_ROTATE_AND_CROP. للحصول على قائمة بالقيم المسموح بها، يمكنك الرجوع إلى CameraCharacteristics#SCALER_AVAILABLE_ROTATE_AND_CROP_MODES.

الكاميراX

مكتبة Jetpack CameraX فإن إنشاء عدسة كاميرا تلائم اتجاه أداة الاستشعار على تدوير الجهاز لمهمة بسيطة.

عنصر التنسيق PreviewView تنشئ معاينة للكاميرا، وتضبط تلقائيًا اتجاه أداة الاستشعار وتدوير الجهاز والتحجيم. تحتفظ PreviewView بنسبة العرض إلى الارتفاع الكاميرا من خلال تطبيق FILL_CENTER نوع تغيير الحجم، والذي يوسيط الصورة ولكن قد يتم اقتصاصها لتتوافق مع الأبعاد من PreviewView. لعرض صورة الكاميرا بتنسيق شاشة عريضة أفقيًا، اضبط نوع المقياس على FIT_CENTER.

للتعرّف على أساسيات إنشاء معاينة للكاميرا باستخدام PreviewView، اطّلِع على مقالة تنفيذ معاينة.

للحصول على نموذج كامل لعملية التنفيذ، يمكنك الاطّلاع على CameraXBasic على جيت هب.

عدسة الكاميرا

على غرار حالة استخدام المعاينة، توفر مكتبة CameraViewfinder مجموعة من الأدوات لتبسيط إنشاء معاينة الكاميرا. ولا يعتمد هذا الإصدار على CameraX Core، لذا يمكنك دمجه بسلاسة في قاعدة بيانات Camera2 الحالية.

بدلاً من استخدام Surface مباشرةً، يمكنك استخدام CameraViewfinder التطبيق المصغَّر لعرض خلاصة الكاميرا على Camera2.

يستخدم CameraViewfinder داخليًا إما TextureView أو SurfaceView لعرض خلاصة الكاميرا، ويطبّق عليها عمليات التحويل المطلوبة لعرض عدسة الكاميرا بشكل صحيح. ويتضمن ذلك تصحيح نسبة العرض إلى الارتفاع والمقياس والتدوير.

لطلب السطح من الكائن CameraViewfinder، عليك: إنشاء ViewfinderSurfaceRequest

يتضمّن هذا الطلب متطلبات درجة دقة السطح وجهاز الكاميرا. معلومات واردة من CameraCharacteristics.

جارٍ الاتصال بالرقم requestSurfaceAsync() يرسل الطلب إلى موفّر مساحة العرض، وهو إما TextureView أو SurfaceView ويحصل على ListenableFuture مقابل Surface.

جارٍ الاتصال بالرقم markSurfaceSafeToRelease() إلى موفّر مساحة العرض أنّ السطح غير ضروري ذا صلة من الموارد.

KotlinJava
fun startCamera(){
    val previewResolution = Size(width, height)
    val viewfinderSurfaceRequest =
        ViewfinderSurfaceRequest(previewResolution, characteristics)
    val surfaceListenableFuture =
        cameraViewfinder.requestSurfaceAsync(viewfinderSurfaceRequest)

    Futures.addCallback(surfaceListenableFuture, object : FutureCallback<Surface> {
        override fun onSuccess(surface: Surface) {
            /* create a CaptureSession using this surface as usual */
        }
        override fun onFailure(t: Throwable) { /* something went wrong */}
    }, ContextCompat.getMainExecutor(context))
}
    void startCamera(){
        Size previewResolution = new Size(width, height);
        ViewfinderSurfaceRequest viewfinderSurfaceRequest =
                new ViewfinderSurfaceRequest(previewResolution, characteristics);
        ListenableFuture<Surface> surfaceListenableFuture =
                cameraViewfinder.requestSurfaceAsync(viewfinderSurfaceRequest);

        Futures.addCallback(surfaceListenableFuture, new FutureCallback<Surface>() {
            @Override
            public void onSuccess(Surface result) {
                /* create a CaptureSession using this surface as usual */
            }
            @Override public void onFailure(Throwable t) { /* something went wrong */}
        },  ContextCompat.getMainExecutor(context));
    }

تقنية SurfaceView

SurfaceView هو مباشرةً لإنشاء معاينة للكاميرا إذا لم يتم تتطلب معالجة ولا تكون متحركة.

يُدير SurfaceView تلقائيًا ذاكرة التخزين المؤقت لصورة استشعار الكاميرا لمطابقة اتجاه الشاشة، مع مراعاة اتجاه استشعار الكاميرا واتجاه الجهاز. ومع ذلك، يتم تغيير حجم ذاكرة التخزين المؤقت للصورة لتلائم أبعاد SurfaceView بدون مراعاة نسبة العرض إلى الارتفاع.

يجب التأكد من أن نسبة العرض إلى الارتفاع للمخزن المؤقت للصور تطابق العرض إلى الارتفاع نسبة SurfaceView، التي يمكنك تحقيقها من خلال تغيير حجم المحتوى SurfaceView في هيكل المكوِّن onMeasure() :

(رمز المصدر computeRelativeRotation() متوفر في التدوير النسبي أدناه).

KotlinJava
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
    val width = MeasureSpec.getSize(widthMeasureSpec)
    val height = MeasureSpec.getSize(heightMeasureSpec)

    val relativeRotation = computeRelativeRotation(characteristics, surfaceRotationDegrees)

    if (previewWidth > 0f && previewHeight > 0f) {
        /* Scale factor required to scale the preview to its original size on the x-axis. */
        val scaleX =
            if (relativeRotation % 180 == 0) {
                width.toFloat() / previewWidth
            } else {
                width.toFloat() / previewHeight
            }
        /* Scale factor required to scale the preview to its original size on the y-axis. */
        val scaleY =
            if (relativeRotation % 180 == 0) {
                height.toFloat() / previewHeight
            } else {
                height.toFloat() / previewWidth
            }

        /* Scale factor required to fit the preview to the SurfaceView size. */
        val finalScale = min(scaleX, scaleY)

        setScaleX(1 / scaleX * finalScale)
        setScaleY(1 / scaleY * finalScale)
    }
    setMeasuredDimension(width, height)
}
@Override
void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int width = MeasureSpec.getSize(widthMeasureSpec);
    int height = MeasureSpec.getSize(heightMeasureSpec);

    int relativeRotation = computeRelativeRotation(characteristics, surfaceRotationDegrees);

    if (previewWidth > 0f && previewHeight > 0f) {

        /* Scale factor required to scale the preview to its original size on the x-axis. */
        float scaleX = (relativeRotation % 180 == 0)
                       ? (float) width / previewWidth
                       : (float) width / previewHeight;

        /* Scale factor required to scale the preview to its original size on the y-axis. */
        float scaleY = (relativeRotation % 180 == 0)
                       ? (float) height / previewHeight
                       : (float) height / previewWidth;

        /* Scale factor required to fit the preview to the SurfaceView size. */
        float finalScale = Math.min(scaleX, scaleY);

        setScaleX(1 / scaleX * finalScale);
        setScaleY(1 / scaleY * finalScale);
    }
    setMeasuredDimension(width, height);
}

لمزيد من التفاصيل حول تنفيذ SurfaceView كعرض كاميرا، يُرجى الاطّلاع على اتجاهات الكاميرا.

TextureView

TextureView أقل أداءً من SurfaceView والمزيد من العمل، إلا أنّ TextureView يمنحك الحد الأقصى التحكم في معاينة الكاميرا.

TextureView تُدير وحدة تخزين مؤقت لصور المستشعر استنادًا إلى اتجاه المستشعر، ولكنها لا تتعامل مع تدوير الجهاز أو تغيير حجم المعاينة.

يمكن ترميز عمليات التكبير والتصغير والدوران في عملية تحويل مصفوفة. للتعرّف على كيفية تغيير حجم TextureView وتدويره بشكل صحيح، راجع إتاحة الأسطح التي يمكن تغيير حجمها في تطبيق الكاميرا

التدوير النسبي

التدوير النسبي لأداة استشعار الكاميرا هي مقدار التدوير المطلوب محاذاة مخرجات أداة استشعار الكاميرا مع اتجاه الجهاز

يتم استخدام التدوير النسبي بواسطة مكونات مثل SurfaceView وTextureView لتحديد عاملي التحجيم x وy لصورة المعاينة. ويُستخدَم أيضًا لتحديد دوران مخزن مؤقت لصورة أداة الاستشعار.

تتيح فئتَا CameraCharacteristics و Surface احتساب الزاوية النسبية للدوران في أداة استشعار الكاميرا:

KotlinJava
/**
 * Computes rotation required to transform the camera sensor output orientation to the
 * device's current orientation in degrees.
 *
 * @param characteristics The CameraCharacteristics to query for the sensor orientation.
 * @param surfaceRotationDegrees The current device orientation as a Surface constant.
 * @return Relative rotation of the camera sensor output.
 */
public fun computeRelativeRotation(
    characteristics: CameraCharacteristics,
    surfaceRotationDegrees: Int
): Int {
    val sensorOrientationDegrees =
        characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION)!!

    // Reverse device orientation for back-facing cameras.
    val sign = if (characteristics.get(CameraCharacteristics.LENS_FACING) ==
        CameraCharacteristics.LENS_FACING_FRONT
    ) 1 else -1

    // Calculate desired orientation relative to camera orientation to make
    // the image upright relative to the device orientation.
    return (sensorOrientationDegrees - surfaceRotationDegrees * sign + 360) % 360
}
/**
 * Computes rotation required to transform the camera sensor output orientation to the
 * device's current orientation in degrees.
 *
 * @param characteristics The CameraCharacteristics to query for the sensor orientation.
 * @param surfaceRotationDegrees The current device orientation as a Surface constant.
 * @return Relative rotation of the camera sensor output.
 */
public int computeRelativeRotation(
    CameraCharacteristics characteristics,
    int surfaceRotationDegrees
){
    Integer sensorOrientationDegrees =
        characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);

    // Reverse device orientation for back-facing cameras.
    int sign = characteristics.get(CameraCharacteristics.LENS_FACING) ==
        CameraCharacteristics.LENS_FACING_FRONT ? 1 : -1;

    // Calculate desired orientation relative to camera orientation to make
    // the image upright relative to the device orientation.
    return (sensorOrientationDegrees - surfaceRotationDegrees * sign + 360) % 360;
}

مقاييس النوافذ

يجب عدم استخدام حجم الشاشة لتحديد أبعاد عدسة الكاميرا المخصّصة للعرض، لأنّ تطبيق الكاميرا قد يكون قيد التشغيل في جزء من الشاشة، إما في وضع "نوافذ متعدّدة" على الأجهزة الجوّالة أو في وضع "العرض بدون حدود" على ChromeOS.

WindowManager#getCurrentWindowMetrics() (تمت إضافته في المستوى 30 لواجهة برمجة التطبيقات) يعرض حجم نافذة التطبيق بدلاً من حجم الشاشة. توفّر طُرق مكتبة Jetpack WindowManager WindowMetricsCalculator#computeCurrentWindowMetrics() و WindowInfoTracker#currentWindowMetrics() دعمًا مشابهًا مع التوافق مع الإصدارات القديمة حتى المستوى 14 لواجهة برمجة التطبيقات.

التدوير بمقدار 180 درجة

تدوير الجهاز بزاوية 180 درجة (على سبيل المثال، من الاتجاه الطبيعي إلى الاتجاه الطبيعي مقلوبًا) لا يؤدي إلى onConfigurationChanged() معاودة الاتصال. نتيجةً لذلك، قد تظهر معاينة الكاميرا بشكل معكوس.

لاكتشاف الدوران بزاوية 180 درجة، نفِّذ DisplayListener والتحقق من دوران الجهاز من خلال طلب Display#getRotation() في onDisplayChanged() معاودة الاتصال.

المراجع الحصرية

قبل الإصدار 10 من Android، كان النشاط المرئي في أعلى الشاشة فقط في بيئة النوافذ المتعدّدة في حالة RESUMED. كان هذا مربكًا للمستخدمين لأن لم يقدم النظام أي إشارة إلى النشاط الذي تم استئنافه.

طرح نظام التشغيل Android 10 (المستوى 29 من واجهة برمجة التطبيقات) ميزة "استئناف التطبيقات المتعدّدة" التي تكون فيها جميع الأنشطة المرئية في الحالة RESUMED. يمكن أن تنتقل الأنشطة المرئية إلى الحالة PAUSED إذا كان هناك نشاط شفاف فوق النشاط أو إذا كان النشاط لا يمكن التركيز عليه، مثل وضع "نافذة ضمن النافذة" (راجِع إتاحة وضع "نافذة ضمن النافذة").

يجب أن يتيح التطبيق الذي يستخدم الكاميرا أو الميكروفون أو أي مورد حصري أو وحيد على مستوى واجهة برمجة التطبيقات 29 أو إصدار أحدث ميزة "استئناف التطبيقات المتعددة". بالنسبة على سبيل المثال، إذا أرادت ثلاثة أنشطة تم استئنافها استخدام الكاميرا، فلا يمكن إلا إجراء واحد للوصول إلى هذا المورد الحصري. يجب أن ينفِّذ كل نشاط callback onDisconnected() للتأكّد من الوصول المُسبَق إلى الكاميرا من خلال نشاط ذو أولوية أعلى.

لمزيد من المعلومات، يُرجى مراجعة السير الذاتية المتعددة:

مصادر إضافية