कैमरे के ओरिएंटेशन

अगर आपका Android ऐप्लिकेशन कैमरों का इस्तेमाल करता है, तो ओरिएंटेशन को मैनेज करते समय कुछ खास बातों का ध्यान रखना होता है. इस दस्तावेज़ में यह मान लिया गया है कि आपको Android camera2 API के बुनियादी सिद्धांतों के बारे में जानकारी है. camera2 की खास जानकारी पाने के लिए, हमारी ब्लॉग पोस्ट या खास जानकारी पढ़ें. हमारा यह भी सुझाव है कि इस दस्तावेज़ को पढ़ने से पहले, कैमरा ऐप्लिकेशन लिखने की कोशिश करें.

बैकग्राउंड

Android कैमरा ऐप्लिकेशन में ओरिएंटेशन को हैंडल करना मुश्किल होता है. इसके लिए, इन बातों को ध्यान में रखना ज़रूरी है:

  • नैचुरल ओरिएंटेशन: डिवाइस के डिज़ाइन के हिसाब से, 'सामान्य' स्थिति में होने पर डिसप्ले का ओरिएंटेशन. आम तौर पर, मोबाइल फ़ोन के लिए पोर्ट्रेट ओरिएंटेशन और लैपटॉप के लिए लैंडस्केप ओरिएंटेशन.
  • सेंसर का ओरिएंटेशन: डिवाइस पर फ़िज़िकली माउंट किए गए सेंसर का ओरिएंटेशन.
  • डिसप्ले का घुमाव: डिवाइस को उसकी सामान्य स्थिति से कितना घुमाया गया है.
  • व्यूफ़ाइंडर का साइज़: कैमरे की झलक दिखाने के लिए इस्तेमाल किए गए व्यूफ़ाइंडर का साइज़.
  • कैमरे से आउटपुट की गई इमेज का साइज़.

इन फ़ैक्टर की वजह से, कैमरा ऐप्लिकेशन के लिए यूज़र इंटरफ़ेस (यूआई) और प्रीव्यू कॉन्फ़िगरेशन की संख्या बहुत ज़्यादा हो जाती है. इस दस्तावेज़ का मकसद यह दिखाना है कि डेवलपर, Android ऐप्लिकेशन में कैमरे के ओरिएंटेशन को कैसे नेविगेट कर सकते हैं और उन्हें सही तरीके से मैनेज कर सकते हैं.

आसानी के लिए, मान लें कि सभी उदाहरणों में पीछे की ओर लगे कैमरे का इस्तेमाल किया गया है. हालांकि, अगर किसी उदाहरण में ऐसा नहीं है, तो उसके बारे में बताया जाएगा. इसके अलावा, यहां दी गई सभी फ़ोटो को सिमुलेट किया गया है, ताकि इलस्ट्रेशन को ज़्यादा साफ़ तौर पर दिखाया जा सके.

ओरिएंटेशन के बारे में पूरी जानकारी

नैचुरल ओरिएंटेशन

डिवाइस को जिस तरह से इस्तेमाल किया जाता है उस स्थिति में डिसप्ले की दिशा को नैचुरल ओरिएंटेशन कहा जाता है. फ़ोन के लिए, आम तौर पर पोर्ट्रेट मोड का इस्तेमाल किया जाता है. दूसरे शब्दों में कहें, तो फ़ोन की चौड़ाई कम और लंबाई ज़्यादा होती है. लैपटॉप के लिए, उनका नैचुरल ओरिएंटेशन लैंडस्केप होता है. इसका मतलब है कि उनकी चौड़ाई ज़्यादा और लंबाई कम होती है. टैबलेट में, पोर्ट्रेट या लैंडस्केप मोड में वीडियो देखे जा सकते हैं.

फ़ोन, लैपटॉप, और देखने वाले की तरफ़ से किसी ऑब्जेक्ट के साथ नैचुरल ओरिएंटेशन का इलस्ट्रेशन

सेंसर की दिशा

आसान शब्दों में कहें, तो सेंसर ओरिएंटेशन को डिग्री में मापा जाता है. यह इस बात पर निर्भर करता है कि डिवाइस के नैचुरल ओरिएंटेशन से मैच करने के लिए, सेंसर से मिली आउटपुट इमेज को घड़ी की सुई की दिशा में कितने डिग्री घुमाना होगा. दूसरे शब्दों में कहें, तो सेंसर ओरिएंटेशन, डिवाइस पर सेंसर को माउंट करने से पहले, उसे घड़ी की उल्टी दिशा में घुमाए गए डिग्री की संख्या होती है. स्क्रीन पर देखने पर, रोटेशन घड़ी की सुई की दिशा में दिखता है. ऐसा इसलिए होता है, क्योंकि रियर-कैमरा सेंसर, डिवाइस के "बैक" साइड पर इंस्टॉल होता है.

Android 10 के साथ काम करने वाले डिवाइसों के लिए, 7.5.5 कैमरा ओरिएंटेशन की परिभाषा के मुताबिक, सामने और पीछे वाले कैमरे "इस तरह से ओरिएंट किए जाने चाहिए कि कैमरे का लंबा डाइमेंशन, स्क्रीन के लंबे डाइमेंशन के साथ अलाइन हो.".

कैमरों से मिलने वाले आउटपुट बफ़र, लैंडस्केप साइज़ के होते हैं. आम तौर पर, फ़ोन का ओरिएंटेशन पोर्ट्रेट होता है. इसलिए, सेंसर का ओरिएंटेशन आम तौर पर 90 या 270 डिग्री होता है, ताकि आउटपुट बफ़र की लंबी साइड, स्क्रीन की लंबी साइड से मेल खाए. जिन डिवाइसों का ओरिएंटेशन लैंडस्केप होता है उनके लिए सेंसर का ओरिएंटेशन अलग होता है. जैसे, Chromebook. इन डिवाइसों में, इमेज सेंसर को इस तरह से रखा जाता है कि आउटपुट बफ़र की लंबी साइड, स्क्रीन की लंबी साइड से मैच करे. ये दोनों लैंडस्केप साइज़ के हैं. इसलिए, इनके ओरिएंटेशन एक जैसे हैं और सेंसर का ओरिएंटेशन 0 या 180 डिग्री है.

फ़ोन, लैपटॉप, और देखने वाले की तरफ़ से किसी ऑब्जेक्ट के साथ नैचुरल ओरिएंटेशन का इलस्ट्रेशन

यहां दी गई इमेज में दिखाया गया है कि डिवाइस की स्क्रीन पर, दर्शक को क्या-क्या दिखेगा:

सेंसर के ओरिएंटेशन का उदाहरण. इसमें एक फ़ोन, एक लैपटॉप, और ऑब्ज़र्वर की तरफ़ से एक ऑब्जेक्ट दिखाया गया है

यहां दिया गया सीन देखें:

एक सीन में प्यारा Android फ़िगरीन (बगड्रॉइड) दिख रहा है

फ़ोन लैपटॉप
फ़ोन के बैक कैमरे के सेंसर से देखने पर दिखने वाली इमेज का इलस्ट्रेशन लैपटॉप के बैक कैमरे के सेंसर से देखने पर दिखने वाली इमेज का उदाहरण

फ़ोन पर सेंसर का ओरिएंटेशन आम तौर पर 90 या 270 डिग्री होता है. सेंसर के ओरिएंटेशन को ध्यान में न रखने पर, आपको इस तरह की इमेज मिलेंगी:

फ़ोन लैपटॉप
फ़ोन के बैक कैमरे के सेंसर से देखने पर दिखने वाली इमेज का इलस्ट्रेशन लैपटॉप के बैक कैमरे के सेंसर से देखने पर दिखने वाली इमेज का उदाहरण

मान लें कि घड़ी की उलटी दिशा में सेंसर के ओरिएंटेशन की जानकारी, sensorOrientation वैरिएबल में सेव है. सेंसर के ओरिएंटेशन की वजह से होने वाले बदलाव को ठीक करने के लिए, आपको आउटपुट बफ़र को `sensorOrientation` क्लॉकवाइज़ घुमाना होगा, ताकि ओरिएंटेशन को डिवाइस के नैचुरल ओरिएंटेशन के साथ अलाइन किया जा सके.

Android में, ऐप्लिकेशन अपने कैमरे की झलक दिखाने के लिए TextureView या SurfaceView का इस्तेमाल कर सकते हैं. अगर ऐप्लिकेशन इनका सही तरीके से इस्तेमाल करते हैं, तो दोनों सेंसर ओरिएंटेशन को हैंडल कर सकते हैं. हम आपको यहां दिए गए सेक्शन में बताएंगे कि सेंसर के ओरिएंटेशन को कैसे ध्यान में रखना चाहिए.

डिसप्ले रोटेशन

डिसप्ले रोटेशन को औपचारिक तौर पर, स्क्रीन पर बनाए गए ग्राफ़िक के रोटेशन के तौर पर परिभाषित किया जाता है. यह डिवाइस के नैचुरल ओरिएंटेशन से उसके फ़िज़िकल रोटेशन की विपरीत दिशा में होता है. यहां दिए गए सेक्शन में यह माना गया है कि डिसप्ले रोटेशन, 90 के सभी मल्टीपल हैं. अगर डिसप्ले रोटेशन को उसके ऐब्सलूट डिग्री के हिसाब से वापस लाया जाता है, तो उसे {0, 90, 180, 270} में से सबसे नज़दीकी डिग्री में बदलें.

यहां दिए गए सेक्शन में "डिसप्ले ओरिएंटेशन" का मतलब है कि डिवाइस को लैंडस्केप या पोर्ट्रेट मोड में रखा गया है या नहीं. यह "डिसप्ले रोटेशन" से अलग है.

मान लें कि आपने डिवाइसों को उनकी पिछली पोज़िशन से घड़ी की उल्टी दिशा में 90 डिग्री घुमाया है. जैसा कि यहां दी गई इमेज में दिखाया गया है:

फ़ोन, लैपटॉप, और दर्शक की ओर से किसी ऑब्जेक्ट के साथ डिसप्ले को 90 डिग्री घुमाने का इलस्ट्रेशन

मान लें कि सेंसर के ओरिएंटेशन के आधार पर, आउटपुट बफ़र पहले ही घुमा दिए गए हैं. ऐसे में, आपके पास ये आउटपुट बफ़र होंगे:

फ़ोन लैपटॉप
फ़ोन के बैक कैमरे के सेंसर से देखने पर दिखने वाली इमेज का इलस्ट्रेशन लैपटॉप के बैक कैमरे के सेंसर से देखने पर दिखने वाली इमेज का उदाहरण

अगर डिसप्ले रोटेशन को variable displayRotation में सेव किया जाता है, तो सही इमेज पाने के लिए, आपको आउटपुट बफ़र को displayRotation के हिसाब से घड़ी की उल्टी दिशा में घुमाना चाहिए.

फ़्रंट कैमरे के लिए, डिसप्ले रोटेशन, स्क्रीन के हिसाब से इमेज बफ़र पर उल्टी दिशा में काम करता है. अगर आपको फ़्रंट कैमरे से काम करना है, तो आपको डिसप्ले रोटेशन के हिसाब से बफ़र को घड़ी की सुई की दिशा में घुमाना होगा.

सीमाएं

डिसप्ले रोटेशन से, डिवाइस के घड़ी की उल्टी दिशा में घूमने का पता चलता है. यह सभी ओरिएंटेशन/रोटेशन एपीआई के लिए सही नहीं है.

जैसे, मुझे पता चला कि

  • Display#getRotation() का इस्तेमाल करने पर, आपको इस दस्तावेज़ में बताए गए तरीके से घड़ी की सुई की उलटी दिशा में रोटेशन मिलेगा.
  • अगर OrientationEventListener#onOrientationChanged(int) का इस्तेमाल किया जाता है, तो आपको क्लॉकवाइज़ रोटेशन मिलेगा.

यहां ध्यान देने वाली ज़रूरी बात यह है कि डिसप्ले रोटेशन, नैचुरल ओरिएंटेशन के हिसाब से होता है. उदाहरण के लिए, अगर किसी फ़ोन को 90 या 270 डिग्री घुमाया जाता है, तो आपको लैंडस्केप मोड में स्क्रीन दिखेगी. इसके उलट, अगर लैपटॉप को उतना ही घुमाया जाता है, तो आपको पोर्ट्रेट के आकार वाली स्क्रीन दिखेगी. ऐप्लिकेशन को हमेशा इस बात का ध्यान रखना चाहिए और डिवाइस के नैचुरल ओरिएंटेशन के बारे में कभी भी अनुमान नहीं लगाना चाहिए.

उदाहरण

आइए, पिछले आंकड़ों का इस्तेमाल करके यह समझते हैं कि ओरिएंटेशन और रोटेशन क्या होते हैं.

फ़ोन और लैपटॉप को घुमाए बिना, ऑब्जेक्ट के साथ ओरिएंटेशन दिखाने वाला इलस्ट्रेशन

फ़ोन लैपटॉप
नैचुरल ओरिएंटेशन = पोर्ट्रेट नैचुरल ओरिएंटेशन = लैंडस्केप
Sensor Orientation = 90 Sensor Orientation = 0
Display Rotation = 0 Display Rotation = 0
Display Orientation = Portrait Display Orientation = Landscape

फ़ोन और लैपटॉप को घुमाए बिना, ऑब्जेक्ट के साथ ओरिएंटेशन दिखाने वाला इलस्ट्रेशन

फ़ोन लैपटॉप
नैचुरल ओरिएंटेशन = पोर्ट्रेट नैचुरल ओरिएंटेशन = लैंडस्केप
Sensor Orientation = 90 Sensor Orientation = 0
Display Rotation = 90 Display Rotation = 90
Display Orientation = Landscape Display Orientation = Portrait

व्यूफ़ाइंडर का साइज़

ऐप्लिकेशन को ओरिएंटेशन, रोटेशन, और स्क्रीन रिज़ॉल्यूशन के आधार पर, व्यूफ़ाइंडर का साइज़ हमेशा बदलना चाहिए. आम तौर पर, ऐप्लिकेशन को व्यूफ़ाइंडर के ओरिएंटेशन को डिसप्ले के मौजूदा ओरिएंटेशन के जैसा ही रखना चाहिए. दूसरे शब्दों में, ऐप्लिकेशन को व्यूफ़ाइंडर के लंबे किनारे को स्क्रीन के लंबे किनारे के साथ अलाइन करना चाहिए.

कैमरे के हिसाब से इमेज का आउटपुट साइज़

झलक के लिए इमेज के आउटपुट का साइज़ चुनते समय, आपको ऐसा साइज़ चुनना चाहिए जो व्यूफ़ाइंडर के साइज़ के बराबर हो या उससे थोड़ा बड़ा हो. आम तौर पर, आपको आउटपुट बफ़र को स्केल अप नहीं करना चाहिए. ऐसा करने से पिक्सलेशन की समस्या हो सकती है. आपको ऐसा साइज़ भी नहीं चुनना चाहिए जो बहुत बड़ा हो. इससे परफ़ॉर्मेंस कम हो सकती है और बैटरी ज़्यादा खर्च हो सकती है.

JPEG ओरिएंटेशन

आइए, एक सामान्य स्थिति से शुरुआत करते हैं. जैसे, जेपीईजी फ़ोटो कैप्चर करना. camera2 API में, कैप्चर करने के अनुरोध में JPEG_ORIENTATION पास किया जा सकता है. इससे यह तय किया जा सकता है कि आपको अपने आउटपुट JPEG को कितना क्लॉकवाइज़ घुमाना है.

हमने जो जानकारी दी है उसके बारे में यहां खास जानकारी दी गई है:

  • सेंसर के ओरिएंटेशन को मैनेज करने के लिए, आपको इमेज बफ़र को घड़ी की सुई की दिशा में sensorOrientation डिग्री घुमाना होगा.
  • डिसप्ले रोटेशन को मैनेज करने के लिए, आपको बफ़र को displayRotation डिग्री घुमाना होगा. पीछे वाले कैमरे के लिए, इसे घड़ी की सुई की उलटी दिशा में घुमाएं. वहीं, सामने वाले कैमरे के लिए, इसे घड़ी की सुई की दिशा में घुमाएं.

दोनों फ़ैक्टर को जोड़ने पर, घड़ी की सुई की दिशा में घुमाने के लिए ज़रूरी रकम यह है

  • sensorOrientation - displayRotation पीछे वाले कैमरों के लिए.
  • sensorOrientation + displayRotation सामने वाले कैमरे के लिए.

इस लॉजिक के लिए सैंपल कोड, JPEG_ORIENTATION दस्तावेज़ में देखा जा सकता है. ध्यान दें कि दस्तावेज़ के सैंपल कोड में मौजूद deviceOrientation, डिवाइस के क्लॉकवाइज़ रोटेशन का इस्तेमाल कर रहा है. इसलिए, डिसप्ले रोटेशन के लिए इस्तेमाल किए जाने वाले साइन उलट दिए जाते हैं.

झलक देखें

कैमरे की झलक के बारे में क्या जानकारी है? कोई ऐप्लिकेशन, कैमरे की झलक दिखाने के लिए दो मुख्य तरीकों का इस्तेमाल कर सकता है: SurfaceView और TextureView. इन दोनों के लिए, ओरिएंटेशन को सही तरीके से हैंडल करने के लिए अलग-अलग तरीकों की ज़रूरत होती है.

SurfaceView

आम तौर पर, कैमरा प्रीव्यू के लिए SurfaceView का इस्तेमाल करने का सुझाव दिया जाता है. हालांकि, ऐसा तब ही करें, जब आपको प्रीव्यू बफ़र को प्रोसेस या ऐनिमेट न करना हो. यह TextureView से ज़्यादा बेहतर परफ़ॉर्म करता है और इसमें कम संसाधनों की ज़रूरत होती है.

SurfaceView को लेआउट करना भी अपेक्षाकृत आसान होता है. आपको सिर्फ़ उस SurfaceView के आसपेक्ट रेशियो (लंबाई-चौड़ाई का अनुपात) के बारे में सोचना है जिस पर आपको कैमरे की झलक दिखानी है.

सोर्स

SurfaceView के नीचे, Android प्लैटफ़ॉर्म आउटपुट बफ़र को घुमाता है, ताकि वे डिवाइस के डिसप्ले ओरिएंटेशन से मैच हो सकें. दूसरे शब्दों में कहें, तो यह सेंसर के ओरिएंटेशन और डिस्प्ले रोटेशन, दोनों को ध्यान में रखता है. इसे और आसान शब्दों में समझें. जब हमारा डिसप्ले लैंडस्केप मोड में होता है, तो हमें लैंडस्केप मोड में ही झलक दिखती है. इसी तरह, पोर्ट्रेट मोड में डिसप्ले होने पर, हमें पोर्ट्रेट मोड में झलक दिखती है.

इस बारे में यहां दी गई टेबल में बताया गया है. यहां यह ध्यान रखना ज़रूरी है कि सिर्फ़ डिसप्ले रोटेशन से सोर्स का ओरिएंटेशन तय नहीं होता.

डिसप्ले रोटेशन फ़ोन (नैचुरल ओरिएंटेशन = पोर्ट्रेट) लैपटॉप (नैचुरल ओरिएंटेशन = लैंडस्केप)
0 पोर्ट्रेट के आकार वाली इमेज में, बगड्रॉइड का सिर ऊपर की ओर है लैंडस्केप के आकार वाली इमेज, जिसमें बगड्रॉइड का सिर ऊपर की ओर है
90 लैंडस्केप के आकार वाली इमेज, जिसमें बगड्रॉइड का सिर ऊपर की ओर है पोर्ट्रेट के आकार वाली इमेज में, बगड्रॉइड का सिर ऊपर की ओर है
180 पोर्ट्रेट के आकार वाली इमेज में, बगड्रॉइड का सिर ऊपर की ओर है लैंडस्केप के आकार वाली इमेज, जिसमें बगड्रॉइड का सिर ऊपर की ओर है
270 लैंडस्केप के आकार वाली इमेज, जिसमें बगड्रॉइड का सिर ऊपर की ओर है पोर्ट्रेट के आकार वाली इमेज में, बगड्रॉइड का सिर ऊपर की ओर है

लेआउट

जैसा कि आप देख सकते हैं, SurfaceView हमारे लिए कुछ मुश्किल काम पहले से ही कर देता है. हालांकि, अब आपको व्यूफ़ाइंडर के साइज़ या स्क्रीन पर झलक के साइज़ पर ध्यान देना होगा. SurfaceView, सोर्स बफ़र को अपने डाइमेंशन के हिसाब से अपने-आप स्केल करता है. आपको यह पक्का करना होगा कि व्यूफ़ाइंडर का आसपेक्ट रेशियो, सोर्सबफ़र के आसपेक्ट रेशियो के जैसा ही हो. उदाहरण के लिए, अगर पोर्ट्रेट शेप वाली झलक को लैंडस्केप शेप वाले SurfaceView में फ़िट करने की कोशिश की जाती है, तो आपको कुछ इस तरह की गड़बड़ी दिखेगी:

इमेज में दिखाया गया है कि पोर्ट्रेट मोड में ली गई फ़ोटो की झलक को लैंडस्केप मोड वाले व्यूफ़ाइंडर में फ़िट करने पर, बगड्रॉइड की इमेज कैसी दिखती है

आम तौर पर, व्यूफ़ाइंडर का आसपेक्ट रेशियो (यानी कि चौड़ाई/ऊंचाई), सोर्स के आसपेक्ट रेशियो के बराबर होना चाहिए. अगर आपको व्यूफ़ाइंडर में इमेज को क्लिप नहीं करना है, तो डिसप्ले को ठीक करने के लिए कुछ पिक्सल काट दें. इसके लिए, दो मामलों पर विचार करना होगा. पहला, जब aspectRatioActivity, aspectRatioSource से ज़्यादा हो और दूसरा, जब यह aspectRatioSource से कम या इसके बराबर हो

aspectRatioActivity > aspectRatioSource

इस मामले में, गतिविधि को "ज़्यादा" माना जा सकता है. यहां एक उदाहरण दिया गया है, जिसमें 16:9 ऐक्टिविटी और 4:3 सोर्स है.

aspectRatioActivity = 16/9 ≈ 1.78
aspectRatioSource = 4/3 ≈ 1.33

सबसे पहले, आपको व्यूफ़ाइंडर को भी 4:3 पर सेट करना होगा. इसके बाद, आपको सोर्स और व्यूफ़ाइंडर को इस तरह से ऐक्टिविटी में फ़िट करना होगा:

ऐसी गतिविधि का उदाहरण जिसका आसपेक्ट रेशियो, व्यूफ़ाइंडर के आसपेक्ट रेशियो से ज़्यादा है

इस मामले में, आपको व्यूफ़ाइंडर की ऊंचाई को गतिविधि की ऊंचाई से मैच करना चाहिए. साथ ही, व्यूफ़ाइंडर के आसपेक्ट रेशियो (लंबाई-चौड़ाई का अनुपात) को सोर्स के आसपेक्ट रेशियो के बराबर रखना चाहिए. यहां दिया गया कोड देखें:

viewfinderHeight = activityHeight;
viewfinderWidth = activityHeight * aspectRatioSource;
aspectRatioActivity ≤ aspectRatioSource

दूसरा मामला तब होता है, जब गतिविधि "छोटी" या "लंबी" होती है. हम पिछले उदाहरण का फिर से इस्तेमाल कर सकते हैं. हालांकि, इस उदाहरण में डिवाइस को 90 डिग्री घुमाया जाता है. इससे गतिविधि 9:16 और सोर्स 3:4 हो जाता है.

aspectRatioActivity = 9/16 = 0.5625
aspectRatioSource = 3/4 = 0.75

इस मामले में, आपको सोर्स और व्यूफ़ाइंडर को गतिविधि में इस तरह फ़िट करना होगा:

ऐसी गतिविधि का उदाहरण जिसका आसपेक्ट रेशियो, व्यूफ़ाइंडर के आसपेक्ट रेशियो से कम है

आपको व्यूफ़ाइंडर की चौड़ाई को गतिविधि की चौड़ाई से मैच करना चाहिए. हालांकि, पिछले मामले में ऐसा नहीं था. साथ ही, व्यूफ़ाइंडर के आसपेक्ट रेशियो (लंबाई-चौड़ाई का अनुपात) को सोर्स के आसपेक्ट रेशियो के बराबर रखना चाहिए. स्यूडो कोड:

viewfinderWidth = activityWidth;
viewfinderHeight = activityWidth / aspectRatioSource;
क्लिपिंग

Camera2 के सैंपल से लिया गया AutoFitSurfaceView.kt (github), SurfaceView को बदल देता है. साथ ही, यह दोनों डाइमेंशन में गतिविधि के बराबर या "थोड़ा बड़ा" इमेज का इस्तेमाल करके, आसपेक्ट रेशियो के मेल न खाने की समस्या को ठीक करता है. इसके बाद, यह उस कॉन्टेंट को काट देता है जो तय सीमा से ज़्यादा होता है. यह उन ऐप्लिकेशन के लिए काम की सुविधा है जिन्हें इमेज को खराब किए बिना, पूरी गतिविधि को कवर करने वाली झलक दिखानी होती है या तय डाइमेंशन के व्यू को पूरी तरह से भरना होता है.

चेतावनी

ऊपर दिए गए सैंपल में, स्क्रीन पर मौजूद जगह का ज़्यादा से ज़्यादा इस्तेमाल करने की कोशिश की गई है. इसके लिए, झलक को गतिविधि से थोड़ा बड़ा बनाया गया है, ताकि कोई जगह खाली न रहे. यह इस बात पर निर्भर करता है कि पैरंट लेआउट (या ViewGroup) डिफ़ॉल्ट रूप से, ओवरफ़्लो होने वाले हिस्सों को काट देता है. यह व्यवहार, RelativeLayout और LinearLayout के साथ काम करता है, लेकिन ConstraintLayout के साथ नहीं. ConstraintLayout, चाइल्ड व्यू का साइज़ बदलकर उन्हें लेआउट में फ़िट कर सकता है. इससे "सेंटर-क्रॉप" इफ़ेक्ट नहीं दिखेगा और इमेज स्ट्रेच हो जाएंगी. रेफ़रंस के तौर पर, इस कमिट का इस्तेमाल किया जा सकता है.

TextureView

TextureView से, कैमरे की झलक के कॉन्टेंट पर ज़्यादा कंट्रोल मिलता है. हालांकि, इससे परफ़ॉर्मेंस पर असर पड़ता है. साथ ही, कैमरे की झलक को सही तरीके से दिखाने के लिए, ज़्यादा काम करने की ज़रूरत होती है.

सोर्स

TextureView के नीचे, Android प्लैटफ़ॉर्म सेंसर के ओरिएंटेशन के हिसाब से आउटपुट बफ़र को घुमाता है, ताकि डिवाइस के नैचुरल ओरिएंटेशन से मैच किया जा सके. TextureView, सेंसर ओरिएंटेशन को मैनेज करता है. हालांकि, यह डिसप्ले रोटेशन को मैनेज नहीं करता. यह आउटपुट बफ़र को डिवाइस के नैचुरल ओरिएंटेशन के साथ अलाइन करता है. इसका मतलब है कि आपको डिसप्ले रोटेशन को खुद मैनेज करना होगा.

इस बारे में यहां दी गई टेबल में बताया गया है. आंकड़ों को उनके डिसप्ले रोटेशन के हिसाब से घुमाकर देखें. आपको SurfaceView में वही आंकड़े दिखेंगे.

डिसप्ले रोटेशन फ़ोन (नैचुरल ओरिएंटेशन = पोर्ट्रेट) लैपटॉप (नैचुरल ओरिएंटेशन = लैंडस्केप)
0 पोर्ट्रेट के आकार वाली इमेज में, बगड्रॉइड का सिर ऊपर की ओर है लैंडस्केप के आकार वाली इमेज, जिसमें बगड्रॉइड का सिर ऊपर की ओर है
90 पोर्ट्रेट के आकार वाली इमेज में, बगड्रॉइड का सिर दाईं ओर है लैंडस्केप आकार की इमेज, जिसमें बगड्रॉइड का सिर दाईं ओर है
180 लैंडस्केप के आकार वाली इमेज, जिसमें बगड्रॉइड का सिर ऊपर की ओर है पोर्ट्रेट के आकार वाली इमेज में, बगड्रॉइड का सिर ऊपर की ओर है
270 लैंडस्केप के आकार वाली इमेज, जिसमें बगड्रॉइड का सिर ऊपर की ओर है पोर्ट्रेट के आकार वाली इमेज में, बगड्रॉइड का सिर ऊपर की ओर है

लेआउट

TextureView के मामले में लेआउट थोड़ा मुश्किल होता है. पहले, TextureView के लिए ट्रांसफ़ॉर्मेशन मैट्रिक्स का इस्तेमाल करने का सुझाव दिया गया था. हालांकि, यह तरीका सभी डिवाइसों पर काम नहीं करता. हमारा सुझाव है कि आप यहां दिया गया तरीका अपनाएं.

TextureView पर झलक को सही तरीके से लेआउट करने की तीन चरणों वाली प्रोसेस:

  1. TextureView का साइज़, चुनी गई झलक के साइज़ के बराबर सेट करें.
  2. TextureView को स्केल करके, वापस झलक के ओरिजनल डाइमेंशन पर ले जाएं.
  3. TextureView को घड़ी की उलटी दिशा में displayRotation डिग्री घुमाएं.

मान लें कि आपके पास ऐसा फ़ोन है जिसकी स्क्रीन को 90 डिग्री तक घुमाया जा सकता है.

फ़ोन का इलस्ट्रेशन, जिसमें डिसप्ले को 90 डिग्री पर घुमाया गया है और एक ऑब्जेक्ट दिखाया गया है

1. TextureView का साइज़, चुनी गई झलक के साइज़ के बराबर सेट करें

मान लें कि आपने झलक का साइज़ previewWidth × previewHeight चुना है, जहां previewWidth > previewHeight (सेंसर का आउटपुट लैंडस्केप मोड में होता है). कैप्चर सेशन को कॉन्फ़िगर करते समय, SurfaceTexture#setDefaultBufferSize(int width, height) को कॉल करके, झलक का साइज़ (previewWidth × previewHeight) तय करना चाहिए.

setDefaultBufferSize को कॉल करने से पहले, यह ज़रूरी है कि View#setLayoutParams(android.view.ViewGroup.LayoutParams) की मदद से, TextureView का साइज़ भी`previewWidth × previewHeight`पर सेट किया जाए. इसकी वजह यह है कि TextureView, मेज़र की गई चौड़ाई और ऊंचाई के साथ SurfaceTexture#setDefaultBufferSize(int width, height) को कॉल करता है. अगर TextureView का साइज़ पहले से सेट नहीं किया गया है, तो इससे रेस कंडीशन हो सकती है. इस समस्या को कम करने के लिए, सबसे पहले TextureView का साइज़ सेट करें.

अब ऐसा हो सकता है कि TextureView, सोर्स के डाइमेंशन से मेल न खाए. फ़ोन के मामले में, सोर्स पोर्ट्रेट शेप में होता है. हालांकि, आपने अभी-अभी जो layoutParams सेट किए हैं उनकी वजह से TextureView लैंडस्केप शेप में होता है. इससे झलकियां स्ट्रेच हो जाएंगी. यहां दिखाया गया है कि ऐसा कैसे होगा:

पोर्ट्रेट के आकार की झलक दिखाने वाली इमेज, जिसे झलक के लिए चुने गए साइज़ के TextureView में फ़िट करने के लिए स्ट्रेच किया गया है

2. TextureView को स्केल करके, झलक के ओरिजनल डाइमेंशन पर वापस लाएं

स्ट्रेच की गई झलक को वापस सोर्स के डाइमेंशन पर लाने के लिए, यहां दी गई जानकारी देखें.

सोर्स के डाइमेंशन (sourceWidth × sourceHeight) ये हैं:

  • previewHeight × previewWidth, अगर डिवाइस का ओरिएंटेशन पोर्ट्रेट या रिवर्स-पोर्ट्रेट है (सेंसर का ओरिएंटेशन 90 या 270 डिग्री है)
  • previewWidth × previewHeight, अगर डिवाइस का ओरिएंटेशन लैंडस्केप या रिवर्स-लैंडस्केप है (सेंसर का ओरिएंटेशन 0 या 180 डिग्री है)

View#setScaleX(float) और View#setScaleY(float) का इस्तेमाल करके, इमेज के खिंचने की समस्या ठीक करना

  • setScaleX(sourceWidth / previewWidth)
  • setScaleY(sourceHeight / previewHeight)

इमेज में, स्ट्रेच की गई झलक को उसके मूल डाइमेंशन में वापस लाने की प्रोसेस दिखाई गई है

3. झलक को `displayRotation` counterclockwise के हिसाब से घुमाएं

जैसा कि पहले बताया गया है, डिसप्ले रोटेशन की वजह से होने वाले बदलाव को ठीक करने के लिए, आपको झलक को displayRotation घड़ी की उल्टी दिशा में घुमाना चाहिए.

इसके लिए, View#setRotation(float) पर जाएं

  • setRotation(-displayRotation) का इस्तेमाल करें, क्योंकि यह घड़ी की दिशा में घूमता है.

इमेज में, झलक को डिवाइस के डिसप्ले ओरिएंटेशन के हिसाब से घुमाने की प्रोसेस दिखाई गई है

नमूना
  • Jetpack में camerax से PreviewView, TextureView लेआउट को पहले बताए गए तरीके से हैंडल करता है. यह PreviewCorrector की मदद से ट्रांसफ़ॉर्मेशन को कॉन्फ़िगर करता है.

ध्यान दें: अगर आपने अपने कोड में TextureView के लिए पहले ट्रांसफ़ॉर्मेशन मैट्रिक्स का इस्तेमाल किया है, तो हो सकता है कि Chromebook जैसे लैंडस्केप मोड वाले डिवाइस पर झलक ठीक से न दिखे. ऐसा हो सकता है कि ट्रांसफ़ॉर्मेशन मैट्रिक्स में, सेंसर के ओरिएंटेशन को 90 या 270 डिग्री माना गया हो. समस्या हल करने के तरीके के लिए, GitHub पर मौजूद इस कमिट को देखें. हालांकि, हमारा सुझाव है कि आप अपने ऐप्लिकेशन को यहां बताए गए तरीके का इस्तेमाल करने के लिए माइग्रेट करें.