پیش‌نمایش دوربین

توجه: این صفحه به بسته Camera2 اشاره دارد. توصیه می‌کنیم از CameraX استفاده کنید، مگراینکه برنامه‌تان به ویژگی‌های خاص و سطح پایین Camera2 نیاز داشته باشد. ‫CameraX و Camera2 هر دو از Android 5.0 (سطح میانای برنامه‌سازی کاربردی ۲۱) و بالاتر پشتیبانی می‌کنند.

دوربین‌ها و پیش‌نمایش‌های دوربین همیشه در دستگاه‌های Android جهت یکسانی ندارند.

دوربین در موقعیت ثابتی روی دستگاه قرار دارد، صرف‌نظر از اینکه دستگاه تلفن، رایانه لوحی، یا رایانه باشد. وقتی جهت دستگاه تغییر می‌کند، جهت دوربین هم تغییر می‌کند.

درنتیجه، برنامه‌های دوربین معمولاً رابطه ثابتی بین جهت دستگاه و نسبت ابعادی پیش‌نمایش دوربین فرض می‌کنند. وقتی تلفن در جهت عمودی باشد، فرض می‌شود که پیش‌نمایش دوربین بلندتر از پهنای آن است. وقتی تلفن (و دوربین) به حالت افقی چرخانده می‌شود، انتظار می‌رود پیش‌نمایش دوربین عریض‌تر از ارتفاع آن باشد.

اما این فرض‌ها با ابعاد جدید، مانند دستگاه‌های تاشو، و حالت‌های نمایش مانند چندپنجره‌ای و چندنمایشگری به چالش کشیده می‌شوند. دستگاه‌های تاشو اندازه نمایش و نسبت ابعادی را بدون تغییر جهت تغییر می‌دهند. حالت چندپنجره‌ای برنامه‌های دوربین را به بخشی از صفحه‌نمایش محدود می‌کند و پیش‌نمایش دوربین را بدون توجه به جهت دستگاه مقیاس‌بندی می‌کند. حالت چندنمایشگری امکان استفاده از نمایشگرهای ثانویه را فراهم می‌کند که ممکن است جهتشان با نمایشگر اصلی یکسان نباشد.

جهت دوربین

تعریف سازگاری Android مشخص می‌کند که حسگر تصویر دوربین «باید به‌گونه‌ای جهت‌دهی شود که بُعد بلند دوربین با بُعد بلند صفحه‌نمایش هم‌راستا باشد. یعنی وقتی دستگاه در جهت افقی نگه داشته می‌شود، دوربین‌ها باید تصاویر را در جهت افقی ضبط کنند. این موضوع صرف‌نظر از جهت طبیعی دستگاه اعمال می‌شود؛ یعنی هم برای دستگاه‌های با جهت اصلی افقی و هم برای دستگاه‌های با جهت اصلی عمودی اعمال می‌شود.»

چیدمان دوربین به صفحه‌نمایش، ناحیه نمایش نمایاب دوربین را در برنامه دوربین به حداکثر می‌رساند. همچنین، حسگرهای تصویر معمولاً داده‌های خود را در نسبت‌های ابعادی افقی خروجی می‌دهند که ۴:۳ رایج‌ترین آن‌ها است.

تلفن و حسگر دوربین هر دو در جهت عمودی هستند.
شکل ۱. رابطه معمول جهت تلفن و حسگر دوربین.

جهت طبیعی حسگر دوربین افقی است. در شکل ۱، حسگر دوربین جلو (دوربینی که در همان جهت نمایشگر قرار دارد) ۲۷۰ درجه نسبت به تلفن چرخانده شده است تا با «تعریف سازگاری Android» مطابقت داشته باشد.

برای آشکار کردن چرخش حسگر برای برنامه‌ها، camera2 API شامل یک ثابت SENSOR_ORIENTATION است. در اکثر تلفن‌ها و رایانه‌های لوحی، دستگاه جهت حسگر را برای دوربین‌های جلو ۲۷۰ درجه و برای دوربین‌های پشت ۹۰ درجه (زاویه دید از پشت دستگاه) گزارش می‌کند که لبه بلند حسگر را با لبه بلند دستگاه تراز می‌کند. دوربین‌های رایانه کیفی معمولاً جهت حسگر ۰ یا ۱۸۰ درجه را گزارش می‌کنند.

ازآنجایی‌که حسگرهای تصویر دوربین داده‌های خود (میان‌گیر تصویر) را در جهت طبیعی حسگر (افقی) خروجی می‌دهند، میان‌گیر تصویر باید به تعداد درجه‌های مشخص‌شده توسط SENSOR_ORIENTATION چرخانده شود تا پیش‌نمایش دوربین در جهت طبیعی دستگاه به‌صورت عمودی ظاهر شود. برای دوربین‌های جلو، چرخش پادساعت‌گرد است؛ برای دوربین‌های پشت، ساعت‌گرد.

برای مثال، برای دوربین جلو در شکل ۱، میان‌گیر تصویر تولیدشده توسط حسگر دوربین به این شکل است:

حسگر دوربین به جهت افقی با تصویر
            کج، بالا سمت چپ چرخید.

تصویر باید ۲۷۰ درجه پادساعت‌گرد چرخانده شود تا جهت پیش‌نمایش با جهت دستگاه مطابقت داشته باشد:

حسگر دوربین در جهت عمودی با تصویر صاف.

دوربین پشت میان‌گیر تصویری با همان جهت میان‌گیر بالا تولید می‌کند، اما SENSOR_ORIENTATION ۹۰ درجه است. درنتیجه، میان‌گیر ۹۰ درجه در جهت عقربه‌های ساعت می‌چرخد.

چرخش دستگاه

چرخش دستگاه تعداد درجه‌ای است که دستگاه از جهت طبیعی خود می‌چرخد. برای مثال، تلفنی که در جهت افقی قرار دارد، بسته به جهت چرخش، چرخش دستگاه ۹۰ یا ۲۷۰ درجه دارد.

بافر تصویر حسگر دوربین باید به همان تعداد درجه‌ای که دستگاه می‌چرخد (علاوه‌بر درجه جهت حسگر) بچرخد تا پیش‌نمایش دوربین به‌صورت عمودی ظاهر شود.

محاسبه جهت

جهت مناسب پیش‌نمایش دوربین، جهت حسگر و چرخش دستگاه را درنظر می‌گیرد.

چرخش کلی میان‌گیر تصویر حسگر را می‌توان بااستفاده از فرمول زیر محاسبه کرد:

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

که در آن sign برای دوربین‌های جلو 1 و برای دوربین‌های پشت -1 است.

برای دوربین‌های جلو، میان‌گیر تصویر پادساعت‌گرد (از جهت طبیعی حسگر) چرخانده می‌شود. برای دوربین‌های پشت، میان‌گیر تصویر حسگر در جهت ساعت‌گرد چرخانده می‌شود.

عبارت deviceOrientationDegrees * sign + 360 چرخش دستگاه را از پادساعت‌گرد به ساعت‌گرد برای دوربین‌های پشت تبدیل می‌کند (برای مثال، تبدیل ۲۷۰ درجه پادساعت‌گرد به ۹۰ درجه ساعت‌گرد). عملیات پیمانه نتیجه را به کمتر از ۳۶۰ درجه مقیاس‌بندی می‌کند (برای مثال، مقیاس‌بندی ۵۴۰ درجه چرخش به ۱۸۰).

میاناهای برنامه‌سازی کاربردی مختلف چرخش دستگاه را به‌طور متفاوتی گزارش می‌کنند:

  • Display#getRotation() چرخش پادساعت‌گرد دستگاه را (از دید کاربر) ارائه می‌دهد. این مقدار همان‌طور که هست در فرمول بالا قرار می‌گیرد.
  • OrientationEventListener#onOrientationChanged() چرخش ساعت‌گرد دستگاه را (از دید کاربر) برمی‌گرداند. مقدار را برای استفاده در فرمول بالا منفی کنید.

دوربین‌های جلو

پیش‌نمایش دوربین و حسگر هر دو در جهت افقی هستند، حسگر
            در جهت درست قرار دارد.
شکل ۲. پیش‌نمایش دوربین و حسگر با تلفن ۹۰ درجه چرخیده به جهت افقی.

در اینجا میان‌گیر تصویر تولیدشده توسط حسگر دوربین در شکل ۲ آمده است:

حسگر دوربین در جهت افقی با تصویر عمودی.

بافر باید ۲۷۰ درجه پادساعت‌گرد چرخانده شود تا با جهت حسگر مطابقت داشته باشد (به جهت دوربین در بالا مراجعه کنید):

حسگر دوربین به جهت عمودی چرخید و تصویر در بالا سمت راست به‌صورت افقی قرار گرفت.

سپس میان‌گیر ۹۰ درجه دیگر در جهت پادساعت‌گرد چرخانده می‌شود تا چرخش دستگاه را درنظر بگیرد، که منجر به جهت صحیح پیش‌نمای دوربین در شکل ۲ می‌شود:

حسگر دوربین به جهت افقی با تصویر عمودی چرخید.

در اینجا دوربین به سمت راست در جهت افقی چرخانده شده است:

پیش‌نمای دوربین و حسگر هر دو در جهت افقی هستند، اما حسگر وارونه است.
شکل ۳. پیش‌نمایش دوربین و حسگر با تلفن ۲۷۰ درجه چرخیده (یا ۹۰- درجه) به جهت افقی.

این هم میان‌گیر تصویر:

حسگر دوربین به جهت افقی با تصویر وارونه چرخانده شد.

بافر باید ۲۷۰ درجه پادساعت‌گرد چرخانده شود تا با جهت حسگر مطابقت داشته باشد:

حسگر دوربین برای جهت عمودی با تصویر افقی،
            بالا سمت چپ، رتبه‌بندی شده است.

سپس میان‌گیر ۲۷۰ درجه دیگر در جهت پادساعت‌گرد چرخانده می‌شود تا چرخش دستگاه را درنظر بگیرد:

حسگر دوربین به جهت افقی با تصویر عمودی چرخید.

دوربین‌های پشت

دوربین‌های پشت معمولاً جهت حسگر ۹۰ درجه دارند (همان‌طور که از پشت دستگاه دیده می‌شود). هنگام جهت‌دهی پیش‌نمایش دوربین، میان‌گیر تصویر حسگر به‌اندازه چرخش حسگر در جهت ساعت‌گرد چرخانده می‌شود (نه در جهت پادساعت‌گرد مانند دوربین‌های جلو)، و سپس میان‌گیر تصویر به‌اندازه چرخش دستگاه در جهت پادساعت‌گرد چرخانده می‌شود.

پیش‌نمای دوربین و حسگر هر دو در جهت افقی هستند، اما حسگر وارونه است.
شکل ۴. تلفن با دوربین پشت در جهت افقی (۲۷۰ یا ۹۰- درجه چرخانده شده).

در اینجا میان‌گیر تصویر از حسگر دوربین در شکل ۴ آمده است:

حسگر دوربین به جهت افقی با تصویر وارونه چرخانده شد.

بافر باید ۹۰ درجه ساعت‌گرد چرخانده شود تا با جهت حسگر مطابقت داشته باشد:

حسگر دوربین برای جهت عمودی با تصویر افقی،
            بالا سمت چپ، رتبه‌بندی شده است.

سپس میان‌گیر ۲۷۰ درجه پادساعت‌گرد چرخانده می‌شود تا چرخش دستگاه را درنظر بگیرد:

حسگر دوربین به جهت افقی با تصویر عمودی چرخید.

نسبت ابعادی

نسبت ابعادی نمایشگر زمانی تغییر می‌کند که جهت دستگاه تغییر کند، اما همچنین زمانی که دستگاه‌های تاشو تا و باز می‌شوند، زمانی که اندازه پنجره‌ها در محیط‌های چندپنجره‌ای تغییر می‌کند، و زمانی که برنامه‌ها در نمایشگرهای ثانویه باز می‌شوند.

بافر تصویر حسگر دوربین باید جهت‌دهی و مقیاس‌بندی شود تا با جهت و نسبت ابعادی عنصر میانای کاربر نمایاب مطابقت داشته باشد، زیرا میانای کاربر به‌طور پویا جهت را تغییر می‌دهد—با یا بدون تغییر جهت دستگاه.

در ابعاد جدید یا در محیط‌های چندپنجره‌ای یا چندنمایشگری، اگر برنامه شما فرض کند که پیش‌نمایش دوربین همان جهت دستگاه (عمودی یا افقی) را دارد، ممکن است پیش‌نمایش شما جهت نادرستی داشته باشد، مقیاس نادرستی داشته باشد، یا هر دو.

دستگاه تاشو بازشده با پیش‌نمایش دوربین عمودی که به پهلو چرخیده است.
شکل ۵. دستگاه تاشو از نسبت ابعادی عمودی به افقی تغییر می‌کند اما حسگر دوربین در جهت عمودی باقی می‌ماند.

در شکل ۵، برنامه به‌اشتباه فرض کرده است که دستگاه ۹۰ درجه پادساعت‌گرد چرخانده شده است؛ بنابراین، برنامه پیش‌نمایش را به همان اندازه می‌چرخاند.

دستگاه تاشو بازشده با پیش‌نمایش دوربین به‌صورت عمودی اما فشرده‌شده
            به‌دلیل مقیاس‌بندی نادرست.
شکل ۶. دستگاه تاشو از نسبت ابعادی عمودی به افقی تغییر می‌کند اما حسگر دوربین در جهت عمودی باقی می‌ماند.

در شکل ۶، برنامه نسبت ابعادی میان‌گیر تصویر را تنظیم نکرده است تا بتواند به‌درستی مقیاس‌بندی شود و با ابعاد جدید عنصر میانای کاربر پیش‌نمایش دوربین مطابقت داشته باشد.

برنامه‌های دوربین با جهت ثابت معمولاً در دستگاه‌های تاشو و دیگر دستگاه‌های دارای صفحه‌نمایش بزرگ مانند رایانه‌های کیفی با مشکل مواجه می‌شوند:

پیش‌نمای دوربین در رایانه کیفی عمودی است اما میانای کاربر برنامه افقی است.
شکل ۷. برنامه با جهت عمودی ثابت در رایانه کیفی.

در شکل ۷، واسط کاربر برنامه دوربین به‌صورت افقی است زیرا جهت برنامه فقط به حالت عمودی محدود شده است. تصویر نمایاب نسبت به حسگر دوربین جهت درستی دارد.

حالت چهره درج‌شده

برنامه‌های دوربین که از حالت چندپنجره‌ای پشتیبانی نمی‌کنند (resizeableActivity="false") و جهتشان را محدود می‌کنند (screenOrientation="portrait" یا screenOrientation="landscape") می‌توانند در دستگاه‌های با صفحه‌نمایش بزرگ در حالت چهره درج‌شده قرار بگیرند تا پیش‌نمایش دوربین جهت درستی داشته باشد.

حالت چهره درج‌شده برنامه‌های فقط عمودی را در جهت عمودی درج می‌کند (درج کردن) حتی اگر نسبت ابعادی نمایشگر افقی باشد. برنامه‌های فقط افقی در جهت افقی به‌صورت جعبه‌ای نمایش داده می‌شوند، حتی اگر نسبت ابعادی نمایشگر عمودی باشد. تصویر دوربین برای هم‌راستا شدن با میانای کاربر برنامه چرخانده می‌شود، برای مطابقت با نسبت ابعادی پیش‌نمایش دوربین برش داده می‌شود، و سپس برای پر کردن پیش‌نمایش مقیاس‌بندی می‌شود.

وقتی نسبت ابعادی حسگر تصویر دوربین و نسبت ابعادی فعالیت اصلی برنامه با هم مطابقت نداشته باشند، حالت چهره درج‌شده فعال می‌شود.

پیش‌نمای دوربین و میانای کاربر برنامه در جهت عمودی مناسب در رایانه کیفی.
            تصویر پیش‌نمایش عریض مقیاس‌بندی و برش داده می‌شود تا در جهت عمودی جا شود.
شکل ۸. برنامه عمودی با جهت ثابت در حالت عمودی درج‌شده در رایانه کیفی.

در شکل ۸، برنامه دوربین فقط عمودی چرخانده شده است تا واسط کاربر را به‌صورت عمودی در نمایشگر رایانه کیفی نشان دهد. برنامه به‌دلیل تفاوت در نسبت ابعادی بین برنامه عمودی و نمایشگر افقی، به‌صورت جعبه‌ای نمایش داده می‌شود. تصویر پیش‌نمایش دوربین برای جبران چرخش میانای کاربر برنامه (به‌دلیل حالت چهره درج‌شده) چرخانده شده است و تصویر برای مطابقت با جهت عمودی برش داده شده و مقیاس‌بندی شده است که این امر میدان دید را کاهش می‌دهد.

چرخاندن، برش دادن، مقیاس‌بندی

حالت چهره درج‌شده برای برنامه دوربین فقط چهره در نمایشگری که نسبت ابعادی افقی دارد فراخوانده می‌شود:

پیش‌نمای دوربین در رایانه کیفی عمودی است اما میانای کاربر برنامه افقی است.
شکل ۹. برنامه عمودی با جهت ثابت در رایانه کیفی.

برنامه در جهت عمودی به‌صورت جعبه‌ای نمایش داده می‌شود:

برنامه به جهت عمودی چرخید و حاشیه سیاه به آن اضافه شد. تصویر
            کج است، بالا در سمت راست است.

تصویر دوربین ۹۰ درجه می‌چرخد تا با جهت جدید برنامه مطابقت پیدا کند:

تصویر حسگر ۹۰ درجه چرخانده شد تا عمودی شود.

تصویر به نسبت ابعادی پیش‌نمای دوربین برش داده می‌شود، سپس مقیاس‌بندی می‌شود تا پیش‌نما را پر کند (میدان دید کاهش می‌یابد):

تصویر برش‌خورده دوربین برای پر کردن پیش‌نمایش دوربین مقیاس‌بندی شده است.

در دستگاه‌های تاشو، جهت حسگر دوربین می‌تواند عمودی باشد درحالی‌که نسبت ابعادی نمایشگر افقی است:

پیش‌نمایش دوربین و میانای کاربر برنامه در نمایشگر عریض و بازشده به‌صورت افقی نشان داده می‌شود.
شکل ۱۰. دستگاه بازشده با برنامه دوربین فقط عمودی و نسبت‌های ابعادی متفاوت حسگر دوربین و نمایشگر.

چون پیش‌نمایش دوربین برای تنظیم جهت حسگر چرخانده می‌شود، تصویر در نمایاب به‌درستی جهت‌دهی می‌شود، اما برنامه فقط عمودی به‌صورت افقی است.

حالت چهره درج‌شده فقط باید برنامه را در جهت عمودی به‌صورت جعبه‌ای نمایش دهد تا برنامه و پیش‌نمایش دوربین را به‌درستی جهت‌دهی کند:

برنامه با حاشیه سیاه در جهت عمودی با پیش‌نمایش دوربین
            به‌صورت عمودی در دستگاه تاشو.

API

از Android 12 (میانای برنامه‌سازی کاربردی سطح ۳۱) به بعد، برنامه‌ها می‌توانند به‌طور صریح حالت عمودی درج‌شده را بااستفاده از ویژگی 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 مراجعه کنید.

CameraX

کتابخانه Jetpack CameraX ایجاد نمایاب دوربین را که با جهت حسگر و چرخش دستگاه سازگار باشد به کاری ساده تبدیل می‌کند.

عنصر چیدمان PreviewView پیش‌نمایش دوربین ایجاد می‌کند و به‌طور خودکار برای جهت حسگر، چرخش دستگاه، و مقیاس‌بندی تنظیم می‌شود. PreviewView با اعمال نوع مقیاس FILL_CENTER، نسبت ابعادی تصویر دوربین را حفظ می‌کند. این نوع مقیاس تصویر را در مرکز قرار می‌دهد اما ممکن است آن را برش دهد تا با ابعاد PreviewView مطابقت داشته باشد. برای جعبه‌نامه‌ای کردن تصویر دوربین، نوع مقیاس را روی FIT_CENTER تنظیم کنید.

برای آشنایی با اصول اولیه ایجاد پیش‌نمایش دوربین با PreviewView، پیاده‌سازی پیش‌نمایش را ببینید.

برای پیاده‌سازی نمونه کامل، به CameraXBasic مخزن در GitHub مراجعه کنید.

نمایاب دوربین

مشابه مورد استفاده پیش‌نمایش، کتابخانه CameraViewfinder مجموعه‌ای از ابزارها را برای ساده‌سازی ایجاد پیش‌نمایش دوربین ارائه می‌دهد. این کتابخانه به CameraX Core وابسته نیست، بنابراین می‌توانید آن را به‌طور یکپارچه در پایگاه کد Camera2 موجود خود ادغام کنید.

به‌جای استفاده مستقیم از Surface می‌توانید از ابزاره CameraViewfinder برای نمایش فید دوربین برای Camera2 استفاده کنید.

CameraViewfinder به‌طور داخلی از TextureView یا SurfaceView برای نمایش فید دوربین استفاده می‌کند و تبدیل‌های لازم را روی آن‌ها اعمال می‌کند تا نمایاب را به‌درستی نمایش دهد. این شامل اصلاح نسبت ابعادی، مقیاس، و چرخش آن‌ها می‌شود.

برای درخواست سطح از شیء CameraViewfinder، باید ViewfinderSurfaceRequest ایجاد کنید.

این درخواست حاوی الزامات مربوط به وضوح سطح و اطلاعات دستگاه دوربین از CameraCharacteristics است.

فراخوانی requestSurfaceAsync() درخواست را به ارائه‌دهنده سطح ارسال می‌کند که یا TextureView یا SurfaceView است و ListenableFuture از Surface را دریافت می‌کند.

فراخوانی markSurfaceSafeToRelease() به ارائه‌دهنده سطح اطلاع می‌دهد که به سطح نیازی نیست و منابع مربوطه می‌توانند آزاد شوند.

کاتلین

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() در چرخش نسبی در زیر قرار دارد.)

کاتلین

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 محاسبه چرخش نسبی حسگر دوربین را فعال می‌کنند:

کاتلین

/**
 * 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() (اضافه‌شده در سطح میانای برنامه‌سازی کاربردی ۳۰) اندازه پنجره برنامه را به‌جای اندازه صفحه‌نمایش برمی‌گرداند. روش‌های کتابخانه Jetpack WindowManager WindowMetricsCalculator#computeCurrentWindowMetrics() و WindowInfoTracker#currentWindowMetrics() پشتیبانی مشابهی را با سازگاری با نسخه قدیمی برای سطح API 14 ارائه می‌دهند.

چرخش ۱۸۰ درجه

چرخش ۱۸۰ درجه‌ای دستگاه (برای مثال، از جهت طبیعی به جهت طبیعی وارونه) باعث راه‌اندازی onConfigurationChanged() فراخوانی برگشتی نمی‌شود. درنتیجه، ممکن است پیش‌نمایش دوربین وارونه باشد.

برای تشخیص چرخش ۱۸۰ درجه، DisplayListener را پیاده‌سازی کنید و چرخش دستگاه را با تماس با Display#getRotation() در فراخوانی برگشتی onDisplayChanged() بررسی کنید.

منابع انحصاری

پیش‌از Android 10، فقط بالاترین فعالیت قابل‌مشاهده در محیط چندپنجره‌ای در وضعیت RESUMED بود. این موضوع برای کاربران گیج‌کننده بود زیرا سیستم هیچ نشانه‌ای از اینکه کدام فعالیت ازسر گرفته شده است ارائه نمی‌داد.

Android 10 (سطح میانای برنامه‌سازی کاربردی ۲۹) ازسرگیری چندگانه را معرفی کرد که در آن همه فعالیت‌های نمایان در وضعیت RESUMED هستند. فعالیت‌های قابل‌مشاهده همچنان می‌توانند وارد وضعیت PAUSED شوند، مثلاً اگر فعالیت شفافی روی فعالیت قرار داشته باشد یا فعالیت قابل‌تمرکز نباشد، مانند حالت تصویردرتصویر (به پشتیبانی از تصویردرتصویر مراجعه کنید).

برنامه‌ای که از دوربین، میکروفون، یا هر منبع انحصاری یا تک‌نمونه‌ای در سطح میانای برنامه‌سازی کاربردی ۲۹ یا بالاتر استفاده می‌کند باید از ازسرگیری چندگانه پشتیبانی کند. برای مثال، اگر سه فعالیت ازسرگرفته‌شده بخواهند از دوربین استفاده کنند، فقط یکی از آن‌ها می‌تواند به این منبع انحصاری دسترسی داشته باشد. هر فعالیت باید یک onDisconnected() فراخوان برگشتی را پیاده‌سازی کند تا از دسترسی پیش‌دستانه به دوربین توسط یک فعالیت با اولویت بالاتر مطلع بماند.

برای اطلاعات بیشتر، به ازسرگیری چندگانه مراجعه کنید.

منابع بیشتر