از اندروید ۸.۰ (سطح API ۲۶)، اندروید به فعالیتها اجازه میدهد تا در حالت تصویر در تصویر (PiP) اجرا شوند. PiP نوع خاصی از حالت چند پنجرهای است که بیشتر برای پخش ویدیو استفاده میشود. این حالت به کاربر اجازه میدهد تا هنگام پیمایش بین برنامهها یا مرور محتوا در صفحه اصلی، یک ویدیو را در یک پنجره کوچک پین شده در گوشه صفحه تماشا کند.
قابلیت PiP از APIهای چند پنجرهای موجود در اندروید ۷.۰ برای ارائه پنجره پینشدهی پوشش ویدیو استفاده میکند. برای افزودن PiP به برنامهتان، باید فعالیتهایی را که از PiP پشتیبانی میکنند، ثبت کنید، در صورت نیاز فعالیت خود را به حالت PiP تغییر دهید و مطمئن شوید که عناصر رابط کاربری پنهان هستند و پخش ویدیو در حالت PiP ادامه مییابد.
پنجره PiP در بالاترین لایه صفحه نمایش، در گوشهای که توسط سیستم انتخاب شده است، ظاهر میشود.
قابلیت PiP همچنین در دستگاههای سازگار با سیستم عامل اندروید تیوی که اندروید ۱۴ (سطح API ۳۴) یا بالاتر را اجرا میکنند، پشتیبانی میشود. اگرچه شباهتهای زیادی وجود دارد، اما هنگام استفاده از PiP در تلویزیون ، ملاحظات دیگری نیز باید در نظر گرفته شود.
چگونه کاربران میتوانند با پنجره PiP تعامل داشته باشند
کاربران میتوانند پنجره PiP را به مکان دیگری بکشند. از اندروید ۱۲ به بعد، کاربران همچنین میتوانند:
برای نمایش یک دکمهی تمام صفحه، یک دکمهی بستن، یک دکمهی تنظیمات و اقدامات سفارشی ارائه شده توسط برنامهی شما (مثلاً کنترلهای پخش)، روی پنجره یک بار ضربه بزنید.
برای تغییر اندازه تصویر در تصویر (PiP) فعلی و حداکثر یا حداقل اندازه تصویر در تصویر (PiP)، روی پنجره دو بار ضربه بزنید - برای مثال، دو بار ضربه زدن روی یک پنجره بزرگ شده، آن را کوچک میکند و عکس این قضیه نیز صادق است.
با کشیدن پنجره به لبه چپ یا راست، آن را مخفی کنید. برای برداشتن پنجره از حالت مخفی، یا روی قسمت قابل مشاهده پنجره مخفی شده ضربه بزنید یا آن را به بیرون بکشید.
با استفاده از قابلیت زوم با دو انگشت، اندازه پنجره PiP را تغییر دهید.
برنامه شما کنترل میکند که چه زمانی فعالیت فعلی وارد حالت PiP شود. در اینجا چند مثال آورده شده است:
یک فعالیت میتواند زمانی وارد حالت PiP شود که کاربر دکمه خانه را لمس کند یا صفحه را به سمت بالا بکشد. به این ترتیب است که گوگل مپس در حالی که کاربر همزمان فعالیت دیگری را اجرا میکند، به نمایش مسیرها ادامه میدهد.
برنامه شما میتواند وقتی کاربر برای مرور محتوای دیگر از ویدیو به عقب برمیگردد، ویدیو را به حالت PiP منتقل کند.
برنامه شما میتواند در حالی که کاربر پایان یک قسمت از محتوا را تماشا میکند، ویدیو را به حالت PiP تغییر دهد. صفحه اصلی اطلاعات تبلیغاتی یا خلاصهای درباره قسمت بعدی سریال را نمایش میدهد.
برنامه شما میتواند راهی برای کاربران فراهم کند تا هنگام تماشای ویدیو، محتوای اضافی را در صف انتظار قرار دهند. ویدیو در حالت PiP همچنان پخش میشود در حالی که صفحه اصلی یک فعالیت انتخاب محتوا را نشان میدهد.
اعلام پشتیبانی از PiP
به طور پیشفرض، سیستم به طور خودکار از PiP برای برنامهها پشتیبانی نمیکند. اگر میخواهید PiP در برنامه شما پشتیبانی شود، فعالیت ویدیویی خود را با تنظیم android:supportsPictureInPicture به true در مانیفست خود ثبت کنید. همچنین، مشخص کنید که فعالیت شما تغییرات پیکربندی طرحبندی را مدیریت کند تا فعالیت شما هنگام تغییر طرحبندی در طول انتقال حالت PiP دوباره راهاندازی نشود.
<activity android:name="VideoActivity"
android:supportsPictureInPicture="true"
android:configChanges=
"screenSize|smallestScreenSize|screenLayout|orientation"
...
فعالیت خود را به PiP تغییر دهید
با شروع از اندروید ۱۲، میتوانید با تنظیم پرچم setAutoEnterEnabled روی true ، فعالیت خود را به حالت PiP تغییر دهید. با این تنظیم، یک فعالیت به طور خودکار در صورت نیاز به حالت PiP تغییر میکند، بدون اینکه نیاز باشد صریحاً تابع enterPictureInPictureMode() در onUserLeaveHint فراخوانی کنید. و این مزیت اضافی را دارد که انتقالهای بسیار روانتری را ارائه میدهد. برای جزئیات بیشتر، به Make transitions to PiP mode smoother from gesture navigation مراجعه کنید.
اگر اندروید ۱۱ یا پایینتر را هدف قرار دادهاید، یک اکتیویتی باید تابع enterPictureInPictureMode() برای تغییر به حالت PiP فراخوانی کند. برای مثال، کد زیر وقتی کاربر روی یک دکمه اختصاصی در رابط کاربری برنامه کلیک میکند، یک اکتیویتی را به حالت PiP تغییر میدهد:
کاتلین
override fun onActionClicked(action: Action) { if (action.id.toInt() == R.id.lb_control_picture_in_picture) { activity?.enterPictureInPictureMode() return } }
جاوا
@Override public void onActionClicked(Action action) { if (action.getId() == R.id.lb_control_picture_in_picture) { getActivity().enterPictureInPictureMode(); return; } ... }
شاید بخواهید منطقی را در نظر بگیرید که به جای رفتن به پسزمینه، یک فعالیت را به حالت PiP تغییر دهد. برای مثال، اگر کاربر در حین پیمایش برنامه، دکمه خانه یا برنامههای اخیر را فشار دهد، نقشههای گوگل به حالت PiP تغییر میکنند. میتوانید این مورد را با override کردن onUserLeaveHint() تشخیص دهید:
کاتلین
override fun onUserLeaveHint() { if (iWantToBeInPipModeNow()) { enterPictureInPictureMode() } }
جاوا
@Override public void onUserLeaveHint () { if (iWantToBeInPipModeNow()) { enterPictureInPictureMode(); } }
توصیه شده: یک تجربه انتقال PiP روان را برای کاربران فراهم کنید
اندروید ۱۲ بهبودهای ظاهری قابل توجهی را در انتقالهای انیمیشنی بین پنجرههای تمامصفحه و PiP اضافه کرده است. اکیداً توصیه میکنیم تمام تغییرات قابل اجرا را اعمال کنید؛ پس از انجام این کار، این تغییرات به طور خودکار و بدون نیاز به هیچ کار اضافی، در صفحههای نمایش بزرگ مانند تبلتها و گوشیهای تاشو نیز اعمال میشوند.
اگر برنامه شما شامل بهروزرسانیهای مربوطه نباشد، انتقال تصویر در تصویر (PiP) هنوز کاربردی است، اما انیمیشنها کمتر صیقل داده شدهاند. به عنوان مثال، انتقال از حالت تمام صفحه به حالت PiP میتواند باعث شود پنجره PiP در طول انتقال ناپدید شود و پس از اتمام انتقال دوباره ظاهر شود.
این تغییرات شامل موارد زیر است.
- انتقال روانتر به حالت PiP از طریق ژستهای ناوبری
- تنظیم یک
sourceRectHintمناسب برای ورود و خروج از حالت PiP - غیرفعال کردن تغییر اندازه یکپارچه برای محتوای غیر ویدیویی
برای فعال کردن یک تجربه انتقال روان، به نمونه Android Kotlin PictureInPicture به عنوان مرجع مراجعه کنید.
انتقال به حالت PiP را از طریق ناوبری حرکتی روانتر کنید
از اندروید ۱۲ به بعد، پرچم setAutoEnterEnabled انیمیشن بسیار روانتری را برای انتقال به محتوای ویدیویی در حالت PiP با استفاده از ناوبری حرکتی فراهم میکند - برای مثال، هنگام کشیدن انگشت به بالا از حالت تمام صفحه به صفحه اصلی.
برای ایجاد این تغییر، مراحل زیر را انجام دهید و برای مرجع به این نمونه مراجعه کنید:
از
setAutoEnterEnabledبرای ساختPictureInPictureParams.Builderاستفاده کنید:کاتلین
setPictureInPictureParams(PictureInPictureParams.Builder() .setAspectRatio(aspectRatio) .setSourceRectHint(sourceRectHint) .setAutoEnterEnabled(true) .build())
جاوا
setPictureInPictureParams(new PictureInPictureParams.Builder() .setAspectRatio(aspectRatio) .setSourceRectHint(sourceRectHint) .setAutoEnterEnabled(true) .build());
تابع
setPictureInPictureParamsبا مقدار بهروز شدهیPictureInPictureParamsفراخوانی کنید. برنامه منتظر فراخوانیonUserLeaveHintنمیماند (همانطور که در اندروید ۱۱ انجام میشد).برای مثال، ممکن است بخواهید
setPictureInPictureParamsدر اولین پخش و هر پخش بعدی در صورت تغییر نسبت ابعاد، فراخوانی کنید.تابع
setAutoEnterEnabled(false)را فراخوانی کنید، اما فقط در صورت لزوم. برای مثال، اگر پخش فعلی در حالت مکث باشد، احتمالاً نمیخواهید PiP را وارد کنید.
برای ورود و خروج از حالت PiP، یک sourceRectHint مناسب تنظیم کنید
با معرفی PiP در اندروید ۸.۰، setSourceRectHint ناحیهای از فعالیت را که پس از انتقال به تصویر در تصویر قابل مشاهده است، نشان داد - برای مثال، محدودههای نمایش ویدیو در یک پخشکننده ویدیو.
در اندروید ۱۲، سیستم از sourceRectHint برای پیادهسازی انیمیشن بسیار روانتر، چه هنگام ورود به حالت PiP و چه هنگام خروج از آن، استفاده میکند.
برای تنظیم صحیح sourceRectHint برای ورود و خروج از حالت PiP:
با استفاده از مرزهای مناسب مانند
sourceRectHintPictureInPictureParamsبسازید. همچنین توصیه میکنیم یک شنونده تغییر طرحبندی به پخشکننده ویدیو وصل کنید:کاتلین
val mOnLayoutChangeListener = OnLayoutChangeListener { v: View?, oldLeft: Int, oldTop: Int, oldRight: Int, oldBottom: Int, newLeft: Int, newTop: Int, newRight: Int, newBottom: Int -> val sourceRectHint = Rect() mYourVideoView.getGlobalVisibleRect(sourceRectHint) val builder = PictureInPictureParams.Builder() .setSourceRectHint(sourceRectHint) setPictureInPictureParams(builder.build()) } mYourVideoView.addOnLayoutChangeListener(mOnLayoutChangeListener)
جاوا
private final View.OnLayoutChangeListener mOnLayoutChangeListener = (v, oldLeft, oldTop, oldRight, oldBottom, newLeft, newTop, newRight, newBottom) -> { final Rect sourceRectHint = new Rect(); mYourVideoView.getGlobalVisibleRect(sourceRectHint); final PictureInPictureParams.Builder builder = new PictureInPictureParams.Builder() .setSourceRectHint(sourceRectHint); setPictureInPictureParams(builder.build()); }; mYourVideoView.addOnLayoutChangeListener(mOnLayoutChangeListener);
در صورت لزوم، قبل از شروع انتقال خروج سیستم،
sourceRectHintبهروزرسانی کنید. وقتی سیستم در شرف خروج از حالت PiP است، سلسله مراتب نمای activity به پیکربندی مقصد آن (مثلاً تمام صفحه) تنظیم میشود. برنامه میتواند یک شنونده تغییر طرحبندی را به نمای ریشه یا نمای هدف خود (مانند نمای پخشکننده ویدیو) متصل کند تا رویداد را تشخیص داده وsourceRectHintرا قبل از شروع انیمیشن بهروزرسانی کند.کاتلین
// Listener is called immediately after the user exits PiP but before animating. playerView.addOnLayoutChangeListener { _, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom -> if (left != oldLeft || right != oldRight || top != oldTop || bottom != oldBottom) { // The playerView's bounds changed, update the source hint rect to // reflect its new bounds. val sourceRectHint = Rect() playerView.getGlobalVisibleRect(sourceRectHint) setPictureInPictureParams( PictureInPictureParams.Builder() .setSourceRectHint(sourceRectHint) .build() ) } }
جاوا
// Listener is called right after the user exits PiP but before animating. playerView.addOnLayoutChangeListener((v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> { if (left != oldLeft || right != oldRight || top != oldTop || bottom != oldBottom) { // The playerView's bounds changed, update the source hint rect to // reflect its new bounds. final Rect sourceRectHint = new Rect(); playerView.getGlobalVisibleRect(sourceRectHint); setPictureInPictureParams( new PictureInPictureParams.Builder() .setSourceRectHint(sourceRectHint) .build()); } });
تغییر اندازه یکپارچه را برای محتوای غیر ویدیویی غیرفعال کنید
اندروید ۱۲ پرچم setSeamlessResizeEnabled را اضافه میکند که هنگام تغییر اندازه محتوای غیر ویدیویی در پنجره PiP، انیمیشن محو شدن متقاطع بسیار روانتری را ارائه میدهد. پیش از این، تغییر اندازه محتوای غیر ویدیویی در یک پنجره PiP میتوانست مصنوعات بصری نامطلوبی ایجاد کند.
برای فعال کردن تغییر اندازه یکپارچه برای محتوای ویدیویی:
کاتلین
setPictureInPictureParams(PictureInPictureParams.Builder() .setSeamlessResizeEnabled(true) .build())
جاوا
setPictureInPictureParams(new PictureInPictureParams.Builder() .setSeamlessResizeEnabled(true) .build());
مدیریت رابط کاربری در طول PiP
وقتی اکتیویتی وارد حالت تصویر در تصویر (PiP) میشود یا از آن خارج میشود، سیستم Activity.onPictureInPictureModeChanged() یا Fragment.onPictureInPictureModeChanged() را فراخوانی میکند.
اندروید ۱۵ تغییراتی را معرفی میکند که انتقال روانتر هنگام ورود به حالت PiP را تضمین میکند. این ویژگی برای برنامههایی که عناصر رابط کاربری روی رابط کاربری اصلی خود که به PiP میرود، قرار دارند، مفید است.
توسعهدهندگان از فراخوانی onPictureInPictureModeChanged() برای تعریف منطقی که قابلیت مشاهده عناصر رابط کاربری همپوشانی شده را تغییر میدهد، استفاده میکنند. این فراخوانی زمانی فعال میشود که انیمیشن ورود یا خروج PiP تکمیل شود. از اندروید ۱۵ به بعد، کلاس PictureInPictureUiState شامل یک حالت جدید میشود.
با این حالت جدید رابط کاربری، برنامههایی که اندروید ۱۵ را هدف قرار میدهند، مشاهده میکنند که به محض شروع انیمیشن PiP، فراخوانی تابع Activity#onPictureInPictureUiStateChanged() با isTransitioningToPip() انجام میشود. عناصر رابط کاربری زیادی وجود دارند که در حالت PiP برای برنامه کاربردی نیستند، به عنوان مثال، نماها یا طرحبندیهایی که شامل اطلاعاتی مانند پیشنهادات، ویدیوی آینده، رتبهبندیها و عناوین هستند. وقتی برنامه به حالت PiP میرود، از فراخوانی تابع onPictureInPictureUiStateChanged() برای پنهان کردن این عناصر رابط کاربری استفاده کنید. وقتی برنامه از پنجره PiP به حالت تمام صفحه میرود، از فراخوانی تابع onPictureInPictureModeChanged() برای نمایش مجدد این عناصر استفاده کنید، همانطور که در مثالهای زیر نشان داده شده است:
کاتلین
override fun onPictureInPictureUiStateChanged(pipState: PictureInPictureUiState) { if (pipState.isTransitioningToPip()) { // Hide UI elements. } }
جاوا
@Override public void onPictureInPictureUiStateChanged(PictureInPictureUiState pipState) { if (pipState.isTransitioningToPip()) { // Hide UI elements. } }
کاتلین
override fun onPictureInPictureModeChanged(isInPictureInPictureMode: Boolean) { if (isInPictureInPictureMode) { // Unhide UI elements. } }
جاوا
@Override public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode) { if (isInPictureInPictureMode) { // Unhide UI elements. } }
این تغییر سریع نمایش عناصر رابط کاربری نامربوط (برای یک پنجره PiP) به تضمین یک انیمیشن ورود PiP روانتر و بدون لرزش کمک میکند.
برای ترسیم مجدد عناصر رابط کاربری فعالیت، این فراخوانیهای برگشتی را نادیده بگیرید. به خاطر داشته باشید که در حالت PiP، فعالیت شما در یک پنجره کوچک نمایش داده میشود. کاربران نمیتوانند با عناصر رابط کاربری برنامه شما در حالت PiP تعامل داشته باشند و جزئیات عناصر کوچک رابط کاربری ممکن است به سختی قابل مشاهده باشد. فعالیتهای پخش ویدیو با رابط کاربری حداقلی، بهترین تجربه کاربری را ارائه میدهند.
If your app needs to provide custom actions for PiP, see Add controls on this page. Remove other UI elements before your activity enters PiP and restore them when your activity becomes full screen again.
اضافه کردن کنترلها
پنجره PiP میتواند کنترلها را هنگام باز کردن منوی پنجره توسط کاربر (با ضربه زدن روی پنجره در دستگاه تلفن همراه یا انتخاب منو از کنترل تلویزیون) نمایش دهد.
اگر برنامهای جلسه رسانهای فعالی داشته باشد، کنترلهای پخش، مکث، بعدی و قبلی ظاهر میشوند.
همچنین میتوانید اقدامات سفارشی را به طور صریح با ساخت PictureInPictureParams با PictureInPictureParams.Builder.setActions() قبل از ورود به حالت PiP مشخص کنید و پارامترها را هنگام ورود به حالت PiP با استفاده از enterPictureInPictureMode(android.app.PictureInPictureParams) یا setPictureInPictureParams(android.app.PictureInPictureParams) ارسال کنید. مراقب باشید. اگر سعی کنید بیشتر از getMaxNumPictureInPictureActions() اضافه کنید، فقط حداکثر تعداد را دریافت خواهید کرد.
ادامه پخش ویدیو در حالت PiP
وقتی activity شما به حالت PiP تغییر میکند، سیستم activity را در حالت paused قرار میدهد و متد onPause() activity را فراخوانی میکند. پخش ویدیو نباید متوقف شود و در عوض اگر activity هنگام انتقال به حالت PiP متوقف شود، پخش ادامه مییابد.
در اندروید ۷.۰ و بالاتر، باید پخش ویدیو را متوقف کرده و دوباره از سر بگیرید، زمانی که سیستم، onStop() و onStart() مربوط به اکتیویتی شما را فراخوانی میکند. با انجام این کار، میتوانید از بررسی اینکه آیا برنامه شما در حالت PiP است یا خیر در onPause() و ادامه پخش، اجتناب کنید.
اگر پرچم setAutoEnterEnabled را روی true تنظیم نکردهاید و نیاز دارید که پخش را در پیادهسازی onPause() متوقف کنید، با فراخوانی isInPictureInPictureMode() حالت PiP را بررسی کنید و پخش را به طور مناسب مدیریت کنید. برای مثال:
کاتلین
override fun onPause() { super.onPause() // If called while in PiP mode, do not pause playback. if (isInPictureInPictureMode) { // Continue playback. } else { // Use existing playback logic for paused activity behavior. } }
جاوا
@Override public void onPause() { // If called while in PiP mode, do not pause playback. if (isInPictureInPictureMode()) { // Continue playback. ... } else { // Use existing playback logic for paused activity behavior. ... } }
وقتی فعالیت شما از حالت PiP به حالت تمام صفحه تغییر میکند، سیستم فعالیت شما را از سر میگیرد و متد onResume() شما را فراخوانی میکند.
از یک فعالیت پخش واحد برای PiP استفاده کنید
در برنامه شما، ممکن است کاربر هنگام جستجوی محتوا در صفحه اصلی، در حالی که یک فعالیت پخش ویدیو در حالت PiP است، یک ویدیوی جدید را انتخاب کند. ویدیوی جدید را در فعالیت پخش موجود در حالت تمام صفحه پخش کنید، به جای اینکه یک فعالیت جدید را اجرا کنید که ممکن است کاربر را گیج کند.
برای اطمینان از اینکه یک اکتیویتی واحد برای درخواستهای پخش ویدیو استفاده میشود و در صورت نیاز به حالت PiP یا خارج از آن تغییر میکند، android:launchMode اکتیویتی را در مانیفست خود روی singleTask تنظیم کنید:
<activity android:name="VideoActivity"
...
android:supportsPictureInPicture="true"
android:launchMode="singleTask"
...
در اکتیویتی خود، onNewIntent() را override کنید و ویدیوی جدید را مدیریت کنید و در صورت نیاز، پخش هرگونه ویدیوی موجود را متوقف کنید.
بهترین شیوهها
ممکن است PiP در دستگاههایی که رم کمی دارند غیرفعال باشد. قبل از اینکه برنامه شما از PiP استفاده کند، با فراخوانی hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE) مطمئن شوید که در دسترس است.
PiP برای فعالیتهایی در نظر گرفته شده است که ویدیوی تمام صفحه پخش میکنند. هنگام تغییر فعالیت خود به حالت PiP، از نمایش هر چیزی به جز محتوای ویدیویی خودداری کنید. همانطور که در بخش «مدیریت رابط کاربری در طول PiP» توضیح داده شده است، هنگام ورود فعالیت خود به حالت PiP، آن را ردیابی کرده و عناصر رابط کاربری را پنهان کنید.
وقتی یک فعالیت در حالت PiP است، به طور پیشفرض فوکوس ورودی را دریافت نمیکند. برای دریافت رویدادهای ورودی در حالت PiP، از MediaSession.setCallback() استفاده کنید. برای اطلاعات بیشتر در مورد استفاده از setCallback() به Display a Now Playing card مراجعه کنید.
وقتی برنامه شما در حالت PiP است، پخش ویدیو در پنجره PiP میتواند باعث تداخل صوتی با برنامه دیگری مانند برنامه پخش موسیقی یا برنامه جستجوی صوتی شود. برای جلوگیری از این امر، هنگام شروع پخش ویدیو، درخواست فوکوس صوتی کنید و اعلانهای تغییر فوکوس صوتی را، همانطور که در مدیریت فوکوس صوتی توضیح داده شده است، مدیریت کنید. اگر در حالت PiP اعلانی مبنی بر از دست دادن فوکوس صوتی دریافت کردید، پخش ویدیو را مکث یا متوقف کنید.
وقتی برنامه شما در حال ورود به حالت PiP است، توجه داشته باشید که فقط اکتیویتی بالایی وارد حالت تصویر در تصویر میشود. در برخی موقعیتها، مانند دستگاههای چند پنجرهای، ممکن است اکتیویتی زیرین اکنون نمایش داده شود و دوباره در کنار اکتیویتی PiP قابل مشاهده باشد. شما باید این مورد را به طور مناسب مدیریت کنید، از جمله اینکه اکتیویتی زیرین یک فراخوانی onResume() یا onPause() دریافت کند. همچنین ممکن است کاربر با اکتیویتی تعامل داشته باشد. به عنوان مثال، اگر یک اکتیویتی لیست ویدیو نمایش داده شده و اکتیویتی پخش ویدیو در حالت PiP باشد، کاربر ممکن است یک ویدیوی جدید را از لیست انتخاب کند و اکتیویتی PiP باید بر اساس آن بهروزرسانی شود.
نمونه کد اضافی
برای دانلود یک نمونه برنامه نوشته شده با کاتلین، به نمونه برنامه اندروید PictureInPicture (کاتلین) مراجعه کنید.
