با شروع Android 8.0 (سطح API 26)، Android اجازه می دهد تا فعالیت ها در حالت تصویر در تصویر (PiP) راه اندازی شوند. PiP نوع خاصی از حالت چند پنجره ای است که بیشتر برای پخش ویدیو استفاده می شود. این امکان را به کاربر می دهد که در یک پنجره کوچک که به گوشه ای از صفحه سنجاق شده است، هنگام حرکت بین برنامه ها یا مرور محتوا در صفحه اصلی، ویدیویی را تماشا کند.
PiP از APIهای چندپنجرهای که در Android 7.0 در دسترس هستند برای ارائه پنجره همپوشانی ویدئویی پین شده استفاده میکند. برای افزودن PiP به برنامه خود، باید فعالیتهای خود را که از PiP پشتیبانی میکنند ثبت کنید، فعالیت خود را در صورت نیاز به حالت PiP تغییر دهید، و مطمئن شوید که عناصر رابط کاربری پنهان هستند و پخش ویدیو زمانی که فعالیت در حالت PiP است ادامه مییابد.
پنجره PiP در بالاترین لایه صفحه نمایش، در گوشه ای که توسط سیستم انتخاب شده ظاهر می شود.
PiP همچنین در دستگاههای سازگار با سیستمعامل Android TV دارای Android 14 (سطح API 34) یا بالاتر پشتیبانی میشود. در حالی که شباهت های زیادی وجود دارد، هنگام استفاده از PiP در تلویزیون ملاحظات دیگری نیز وجود دارد.
نحوه تعامل کاربران با پنجره PiP
کاربران می توانند پنجره PiP را به مکان دیگری بکشند. با شروع اندروید 12، کاربران همچنین می توانند:
برای نمایش یک کلید تمام صفحه، یک دکمه بستن، یک دکمه تنظیمات و اقدامات سفارشی ارائه شده توسط برنامه شما (به عنوان مثال، کنترل های پخش) روی پنجره یک ضربه بزنید.
برای جابهجایی بین اندازه PiP فعلی و حداکثر یا حداقل اندازه PiP، دو بار روی پنجره ضربه بزنید - برای مثال، دو بار ضربه زدن روی یک پنجره حداکثر آن را به حداقل میرساند و عکس آن نیز صادق است.
پنجره را با کشیدن آن به لبه چپ یا راست پنهان کنید. برای باز کردن پنجره، روی قسمت قابل مشاهده پنجره پنهان شده ضربه بزنید یا آن را به بیرون بکشید.
اندازه پنجره PiP را با استفاده از کوچک کردن به زوم تغییر دهید.
برنامه شما زمانی را کنترل می کند که فعالیت فعلی وارد حالت PiP شود. در اینجا چند نمونه آورده شده است:
زمانی که کاربر روی دکمه هوم ضربه میزند یا به سمت خانه حرکت میکند، یک فعالیت میتواند وارد حالت PiP شود. به این ترتیب Google Maps به نمایش مسیرها ادامه می دهد در حالی که کاربر فعالیت دیگری را همزمان انجام می دهد.
هنگامی که کاربر از ویدیو برای مرور محتواهای دیگر بازمی گردد، برنامه شما می تواند یک ویدیو را به حالت PiP منتقل کند.
برنامه شما میتواند یک ویدیو را به حالت PiP تغییر دهد در حالی که کاربر پایان یک قسمت از محتوا را تماشا میکند. صفحه اصلی اطلاعات تبلیغاتی یا خلاصه ای از قسمت بعدی سریال را نمایش می دهد.
برنامه شما میتواند راهی برای کاربران فراهم کند تا در هنگام تماشای ویدیو، محتوای اضافی را در صف قرار دهند. پخش ویدیو در حالت PiP ادامه می یابد در حالی که صفحه اصلی فعالیت انتخاب محتوا را نشان می دهد.
پشتیبانی PiP را اعلام کنید
به طور پیش فرض، سیستم به طور خودکار از PiP برای برنامه ها پشتیبانی نمی کند. اگر میخواهید از PiP در برنامه خود پشتیبانی کنید، با تنظیم android:supportsPictureInPicture
روی true
، فعالیت ویدیویی خود را در مانیفست خود ثبت کنید. همچنین، مشخص کنید که فعالیت شما تغییرات پیکربندی چیدمان را انجام دهد تا زمانی که تغییرات طرحبندی در طول انتقال حالت PiP رخ میدهد، فعالیت شما دوباره راهاندازی نشود.
<activity android:name="VideoActivity"
android:supportsPictureInPicture="true"
android:configChanges=
"screenSize|smallestScreenSize|screenLayout|orientation"
...
فعالیت خود را به PiP تغییر دهید
با شروع Android 12، میتوانید با تنظیم پرچم setAutoEnterEnabled
روی true
، فعالیت خود را به حالت PiP تغییر دهید. با این تنظیم، یک اکتیویتی در صورت نیاز به طور خودکار به حالت PiP تغییر می کند، بدون اینکه نیازی به فراخوانی صریح enterPictureInPictureMode()
در onUserLeaveHint
باشد. و این مزیت افزوده ارائه انتقال بسیار نرمتر را دارد. برای جزئیات، از پیمایش اشاره ای، انتقال به حالت PiP را هموارتر کنید .
اگر Android 11 یا پایینتر را هدف قرار میدهید، یک فعالیت باید 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 تغییر میدهد. به عنوان مثال، اگر کاربر در حین پیمایش برنامه، دکمه خانه یا موارد اخیر را فشار دهد، Google Maps به حالت PiP تغییر می کند. شما می توانید این مورد را با نادیده گرفتن onUserLeaveHint()
دریافت کنید:
override fun onUserLeaveHint() {
if (iWantToBeInPipModeNow()) {
enterPictureInPictureMode()
}
}
@Override
public void onUserLeaveHint () {
if (iWantToBeInPipModeNow()) {
enterPictureInPictureMode();
}
}
توصیه میشود: تجربه انتقال PiP را به کاربران ارائه دهید
اندروید 12 بهبودهای زیبایی قابل توجهی را به انتقال متحرک بین پنجره های تمام صفحه و PiP اضافه کرد. ما قویاً توصیه می کنیم همه تغییرات قابل اجرا را اجرا کنید. هنگامی که این کار را انجام دادید، این تغییرات به طور خودکار به صفحه نمایش های بزرگ مانند تاشوها و تبلت ها بدون نیاز به کار بیشتر تبدیل می شوند.
اگر برنامه شما شامل بهروزرسانیهای قابلاجرا نباشد، انتقال PiP همچنان کاربردی است، اما انیمیشنها کمتر صیقلی هستند. به عنوان مثال، انتقال از حالت تمام صفحه به حالت PiP می تواند باعث شود که پنجره PiP در طول انتقال قبل از اینکه پس از تکمیل انتقال دوباره ظاهر شود، ناپدید شود.
این تغییرات شامل موارد زیر است.
- از پیمایش اشاره ای، انتقال به حالت PiP هموارتر می شود
- تنظیم یک
sourceRectHint
مناسب برای ورود و خروج از حالت PiP - غیرفعال کردن تغییر اندازه یکپارچه برای محتوای غیر ویدئویی
به نمونه Android Kotlin PictureInPicture به عنوان مرجع برای فعال کردن یک تجربه انتقال صیقلی مراجعه کنید.
از پیمایش اشاره ای، انتقال به حالت PiP را روان تر کنید
با شروع در اندروید 12، پرچم setAutoEnterEnabled
انیمیشن بسیار نرم تری را برای انتقال به محتوای ویدیویی در حالت PiP با استفاده از ناوبری حرکتی ارائه می دهد - به عنوان مثال، هنگام کشیدن انگشت به سمت خانه از تمام صفحه به سمت بالا.
مراحل زیر را برای ایجاد این تغییر کامل کنید و برای مرجع به این نمونه مراجعه کنید:
برای ساخت
PictureInPictureParams.Builder
ازsetAutoEnterEnabled
استفاده کنید:setPictureInPictureParams(PictureInPictureParams.Builder()
.setAspectRatio(aspectRatio)
.setSourceRectHint(sourceRectHint)
.setAutoEnterEnabled(true)
.build())setPictureInPictureParams(new PictureInPictureParams.Builder()
.setAspectRatio(aspectRatio)
.setSourceRectHint(sourceRectHint)
.setAutoEnterEnabled(true)
.build());با
PictureInPictureParams
بهروز، زودتر باsetPictureInPictureParams
تماس بگیرید. برنامه منتظر تماسonUserLeaveHint
نمی ماند (همانطور که در اندروید 11 انجام می داد).به عنوان مثال، ممکن است بخواهید
setPictureInPictureParams
در اولین پخش و در صورت تغییر نسبت ابعاد، هر پخش بعدی را فراخوانی کنید.با
setAutoEnterEnabled(false)
تماس بگیرید، اما فقط در صورت لزوم. به عنوان مثال، اگر پخش فعلی در حالت مکث است، احتمالاً نمی خواهید PiP را وارد کنید.
یک sourceRectHint
مناسب برای ورود و خروج از حالت PiP تنظیم کنید
با شروع با معرفی PiP در Android 8.0، setSourceRectHint
ناحیهای از فعالیت را نشان داد که پس از انتقال به تصویر در تصویر قابل مشاهده است - به عنوان مثال، محدودیتهای نمای ویدیو در پخشکننده ویدیو.
با اندروید 12، سیستم از sourceRectHint
برای پیاده سازی انیمیشن بسیار روان تری هم هنگام ورود و هم در هنگام خروج از حالت PiP استفاده می کند.
برای تنظیم صحیح sourceRectHint
برای ورود و خروج از حالت PiP:
PictureInPictureParams
با استفاده از کرانهای مناسب به عنوانsourceRectHint
بسازید. همچنین توصیه میکنیم شنونده تغییر طرحبندی را به پخشکننده ویدیو پیوست کنید: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 است، سلسله مراتب نمای فعالیت به پیکربندی مقصد (مثلاً تمام صفحه) نشان داده می شود. این برنامه میتواند شنونده تغییر طرحبندی را به نمای اصلی یا نمای هدف (مانند نمای پخشکننده ویدیو) متصل کند تا رویداد را شناسایی کرده و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());
}
});
تغییر اندازه یکپارچه را برای محتوای غیر ویدئویی غیرفعال کنید
اندروید 12 پرچم setSeamlessResizeEnabled
را اضافه می کند که در هنگام تغییر اندازه محتوای غیر ویدئویی در پنجره PiP، انیمیشن متقاطع بسیار نرم تری ارائه می دهد. قبلاً، تغییر اندازه محتوای غیر ویدئویی در یک پنجره PiP میتوانست مصنوعات بصری دردناکی ایجاد کند.
برای غیرفعال کردن تغییر اندازه یکپارچه برای محتوای غیر ویدئویی:
setPictureInPictureParams(PictureInPictureParams.Builder()
.setSeamlessResizeEnabled(false)
.build())
setPictureInPictureParams(new PictureInPictureParams.Builder()
.setSeamlessResizeEnabled(false)
.build());
مدیریت UI در حین PiP
وقتی اکتیویتی وارد حالت Picture-in-Picture (PiP) می شود یا از آن خارج می شود، سیستم Activity.onPictureInPictureModeChanged()
یا Fragment.onPictureInPictureModeChanged()
فرا می خواند.
اندروید 15 تغییراتی را ارائه می کند که انتقال حتی راحت تر را هنگام ورود به حالت PiP تضمین می کند. این برای برنامههایی مفید است که عناصر UI در بالای رابط کاربری اصلی خود که به PiP میرود، پوشانده شدهاند.
توسعهدهندگان از پاسخ تماس onPictureInPictureModeChanged()
برای تعریف منطقی استفاده میکنند که نمایان شدن عناصر UI روی همپوشانی را تغییر میدهد. این تماس پس از تکمیل انیمیشن ورود یا خروج PiP فعال می شود. با شروع اندروید 15، کلاس PictureInPictureUiState
شامل وضعیت جدیدی می شود.
با این وضعیت رابط کاربری جدید، برنامههایی که Android 15 را هدف قرار میدهند، به محض شروع انیمیشن PiP، پاسخ تماس Activity#onPictureInPictureUiStateChanged()
را با isTransitioningToPip()
فراخوانی میکنند. بسیاری از عناصر رابط کاربری وجود دارند که زمانی که برنامه در حالت PiP قرار دارد، برای برنامه مرتبط نیستند، برای مثال، نماها یا طرحبندی که شامل اطلاعاتی مانند پیشنهادات، ویدیوی آتی، رتبهبندیها و عناوین است. هنگامی که برنامه به حالت PiP می رود، از پاسخ تماس onPictureInPictureUiStateChanged()
برای پنهان کردن این عناصر رابط کاربری استفاده کنید. هنگامی که برنامه از پنجره PiP به حالت تمام صفحه می رود، از callback 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 است، کاربران نمی توانند با عناصر رابط کاربری برنامه شما تعامل داشته باشند و جزئیات عناصر کوچک رابط کاربری ممکن است دشوار باشد. فعالیت های پخش ویدیو با حداقل رابط کاربری بهترین تجربه کاربر را ارائه می دهد.
اگر برنامه شما نیاز به ارائه اقدامات سفارشی برای PiP دارد، به افزودن کنترلها در این صفحه مراجعه کنید. قبل از اینکه فعالیت شما وارد PiP شود، سایر عناصر رابط کاربری را حذف کنید و وقتی فعالیت شما دوباره تمام صفحه شد، آنها را بازیابی کنید.
کنترل ها را اضافه کنید
وقتی کاربر منوی پنجره را باز میکند، پنجره PiP میتواند کنترلها را نمایش دهد (با ضربه زدن روی پنجره روی یک دستگاه تلفن همراه یا انتخاب منو از کنترل از راه دور تلویزیون).
اگر برنامهای یک جلسه رسانه فعال داشته باشد، کنترلهای پخش، مکث، بعدی و قبلی ظاهر میشوند.
همچنین میتوانید با ساخت PictureInPictureParams
با PictureInPictureParams.Builder.setActions()
قبل از وارد شدن به حالت PiP، اکشنهای سفارشی را مشخص کنید و هنگام ورود به حالت PiP با استفاده از enterPictureInPictureMode(android.app.PictureInPictureParams)
یا setPictureInPictureParams(android.app.PictureInPictureParams)
. مراقب باشید. اگر بخواهید بیشتر از getMaxNumPictureInPictureActions()
اضافه کنید، فقط حداکثر عدد را دریافت خواهید کرد.
ادامه پخش ویدیو در حالی که در PiP هستید
هنگامی که فعالیت شما به PiP تغییر می کند، سیستم اکتیویتی را در حالت مکث قرار می دهد و متد onPause()
فعالیت را فراخوانی می کند. پخش ویدیو نباید متوقف شود و در عوض اگر فعالیت در حین انتقال به حالت PiP متوقف شد، به پخش ادامه دهید.
در Android نسخه 7.0 و بالاتر، زمانی که سیستم onStop()
و onStart()
فعالیت شما را فراخوانی میکند، باید پخش ویدیو را متوقف کرده و از سر بگیرید. با انجام این کار، میتوانید از بررسی اینکه آیا برنامه شما در حالت PiP در onPause()
است و صریحاً به پخش ادامه میدهد، اجتناب کنید.
اگر پرچم setAutoEnterEnabled
را روی true
تنظیم نکردهاید و باید پخش را در اجرای onPause()
متوقف کنید، حالت PiP را با فراخوانی isInPictureInPictureMode()
بررسی کنید و پخش را به طور مناسب مدیریت کنید. به عنوان مثال:
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()
را نادیده بگیرید و ویدیوی جدید را مدیریت کنید و در صورت نیاز پخش ویدیوی موجود را متوقف کنید.
بهترین شیوه ها
PiP ممکن است در دستگاههایی که RAM پایینی دارند غیرفعال شود. قبل از اینکه برنامه شما از PiP استفاده کند، با تماس با hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE)
مطمئن شوید که در دسترس است.
PiP برای فعالیت هایی در نظر گرفته شده است که ویدیوهای تمام صفحه را پخش می کنند. هنگامی که فعالیت خود را به حالت PiP تغییر می دهید، از نمایش چیزی به جز محتوای ویدیویی خودداری کنید. زمانی که فعالیت شما وارد حالت PiP میشود، ردیابی کنید و عناصر UI را پنهان کنید، همانطور که در Handling UI در طول PiP توضیح داده شده است.
وقتی یک فعالیت در حالت PiP است، به طور پیشفرض فوکوس ورودی را دریافت نمیکند. برای دریافت رویدادهای ورودی در حالت PiP، از MediaSession.setCallback()
استفاده کنید. برای اطلاعات بیشتر در مورد استفاده از setCallback()
به نمایش کارت در حال پخش مراجعه کنید.
وقتی برنامه شما در حالت PiP است، پخش ویدیو در پنجره PiP میتواند باعث تداخل صوتی با برنامه دیگری، مانند برنامه پخشکننده موسیقی یا برنامه جستجوی صوتی شود. برای جلوگیری از این امر، هنگام شروع پخش ویدیو، فوکوس صوتی را درخواست کنید و همانطور که در مدیریت فوکوس صوتی توضیح داده شده است، اعلانهای تغییر فوکوس صوتی را مدیریت کنید. اگر در حالت PiP اعلان از دست دادن فوکوس صوتی دریافت کردید، پخش ویدیو را متوقف یا متوقف کنید.
هنگامی که برنامه شما میخواهد وارد PiP شود، توجه داشته باشید که تنها فعالیت برتر وارد تصویر در تصویر میشود. در برخی موقعیتها مانند دستگاههای چند پنجرهای، ممکن است فعالیت زیر اکنون در کنار فعالیت PiP نشان داده شود و دوباره قابل مشاهده باشد. شما باید این مورد را بر این اساس مدیریت کنید، از جمله فعالیت زیر دریافت یک onResume()
یا یک پاسخ onPause()
. همچنین ممکن است کاربر با فعالیت تعامل داشته باشد. به عنوان مثال، اگر یک فعالیت لیست ویدیویی نمایش داده شده باشد و فعالیت ویدیویی در حال پخش در حالت PiP باشد، کاربر ممکن است یک ویدیوی جدید را از لیست انتخاب کند و فعالیت PiP باید بر این اساس به روز شود.
کد نمونه اضافی
برای دانلود یک برنامه نمونه نوشته شده در Kotlin، به Android PictureInPicture Sample (Kotlin) مراجعه کنید.