Начиная с 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 в другое место. Начиная с Android 12, пользователи также могут:
Коснитесь окна один раз, чтобы отобразить переключатель полноэкранного режима, кнопку закрытия, кнопку настроек и настраиваемые действия, предоставляемые вашим приложением (например, элементы управления воспроизведением).
Дважды коснитесь окна, чтобы переключиться между текущим размером PiP и максимальным или минимальным размером PiP — например, двойное касание развернутого окна сворачивает его, и обратное также верно.
Скройте окно, перетащив его к левому или правому краю. Чтобы удалить окно, коснитесь видимой части спрятанного окна или перетащите его.
Измените размер окна PiP, используя масштабирование.
Ваше приложение контролирует, когда текущее действие переходит в режим PiP. Вот несколько примеров:
Действие может перейти в режим PiP, когда пользователь нажимает кнопку «Домой» или проводит вверх по экрану «Домой». Таким образом, Карты Google продолжают отображать маршруты, пока пользователь одновременно выполняет другое действие.
Ваше приложение может переместить видео в режим «картинка в картинке», когда пользователь возвращается от видео для просмотра другого контента.
Ваше приложение может переключить видео в режим 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, вы можете переключить свою активность в режим PiP, установив для флага setAutoEnterEnabled
значение true
. При использовании этого параметра действие автоматически переключается в режим PiP по мере необходимости без необходимости явного вызова enterPictureInPictureMode()
в onUserLeaveHint
. И это дает дополнительное преимущество, поскольку обеспечивает гораздо более плавные переходы. Подробную информацию см. в разделе «Как сделать переход в режим PiP более плавным с помощью навигации с помощью жестов» .
Если вы ориентируетесь на Android 11 или более раннюю версию, действие должно вызвать enterPictureInPictureMode()
чтобы переключиться в режим 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 переключаются в режим PiP, если пользователь нажимает кнопку «Домой» или «Недавние» во время навигации по приложению. Вы можете уловить этот случай, переопределив onUserLeaveHint()
:
Котлин
override fun onUserLeaveHint() { if (iWantToBeInPipModeNow()) { enterPictureInPictureMode() } }
Ява
@Override public void onUserLeaveHint () { if (iWantToBeInPipModeNow()) { enterPictureInPictureMode(); } }
Рекомендуется: предоставьте пользователям безупречный опыт перехода в формате PiP.
В Android 12 были внесены значительные косметические улучшения в анимированные переходы между полноэкранным режимом и окнами PiP. Мы настоятельно рекомендуем внести все применимые изменения; как только вы это сделаете, эти изменения автоматически масштабируются на большие экраны, такие как складные устройства и планшеты, без каких-либо дополнительных действий.
Если ваше приложение не содержит соответствующих обновлений, переходы PiP по-прежнему работают, но анимация менее совершенна. Например, переход из полноэкранного режима в режим «картинка в картинке» может привести к исчезновению окна «картинка в картинке» во время перехода, прежде чем оно появится снова после завершения перехода.
Эти изменения заключаются в следующем.
- Переход в режим «картинка в картинке» становится более плавным благодаря навигации с помощью жестов.
- Установка правильного
sourceRectHint
для входа и выхода из режима PiP - Отключение плавного изменения размера для невидеоконтента
Обратитесь к образцу Android Kotlin PictureInPicture в качестве справочного материала по обеспечению более качественного перехода.
Сделайте переходы в режим PiP более плавными благодаря навигации с помощью жестов.
Начиная с Android 12, флаг setAutoEnterEnabled
обеспечивает гораздо более плавную анимацию при переходе к видеоконтенту в режиме «картинка в картинке» с помощью навигации по жестам — например, при пролистывании вверх из полноэкранного режима.
Выполните следующие шаги, чтобы внести это изменение, и обратитесь к этому образцу для справки:
Используйте
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
(как это было бы в Android 11).Например, вы можете вызвать
setPictureInPictureParams
при самом первом воспроизведении и при любом последующем воспроизведении, если соотношение сторон изменено.Вызовите
setAutoEnterEnabled(false)
, но только по мере необходимости. Например, вы, вероятно, не захотите вводить PiP, если текущее воспроизведение находится в состоянии паузы.
Установите правильный sourceRectHint
для входа в режим PiP и выхода из него.
Начиная с появления PiP в Android 8.0, setSourceRectHint
указывал область действия, которая видна после перехода в режим «картинка в картинке» — например, границы просмотра видео в видеоплеере.
В Android 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
до того, как система начнет выходной переход. Когда система собирается выйти из режима «картинка в картинке», иерархия представлений действий выстраивается в соответствии с целевой конфигурацией (например, в полноэкранном режиме). Приложение может подключить прослушиватель изменения макета к своему корневому представлению или целевому представлению (например, представлению видеопроигрывателя), чтобы обнаружить событие и обновить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()); } });
Отключить плавное изменение размера для невидеоконтента
В Android 12 добавлен флаг setSeamlessResizeEnabled
, который обеспечивает более плавную анимацию плавного затухания при изменении размера невидеоконтента в окне PiP. Раньше изменение размера невидеоконтента в окне PiP могло создавать резкие визуальные артефакты.
Чтобы отключить плавное изменение размера невидеоконтента:
Котлин
setPictureInPictureParams(PictureInPictureParams.Builder() .setSeamlessResizeEnabled(false) .build())
Ява
setPictureInPictureParams(new PictureInPictureParams.Builder() .setSeamlessResizeEnabled(false) .build());
Обработка пользовательского интерфейса во время PiP
Когда действие входит в режим «Картинка в картинке» (PiP) или выходит из него, система вызывает Activity.onPictureInPictureModeChanged()
или Fragment.onPictureInPictureModeChanged()
.
В Android 15 представлены изменения, которые обеспечивают еще более плавный переход при переходе в режим PiP. Это полезно для приложений, в которых элементы пользовательского интерфейса наложены поверх основного пользовательского интерфейса, который переходит в PiP.
Разработчики используют обратный вызов onPictureInPictureModeChanged()
для определения логики, которая переключает видимость наложенных элементов пользовательского интерфейса. Этот обратный вызов запускается, когда анимация входа или выхода PiP завершена. Начиная с Android 15, класс PictureInPictureUiState
включает новое состояние.
Благодаря этому новому состоянию пользовательского интерфейса приложения, предназначенные для Android 15, наблюдают, как обратный вызов 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, см. раздел «Добавление элементов управления» на этой странице. Удалите другие элементы пользовательского интерфейса до того, как ваше действие перейдет в PiP, и восстановите их, когда ваше действие снова станет полноэкранным.
Добавить элементы управления
В окне PiP могут отображаться элементы управления, когда пользователь открывает меню окна (прикоснувшись к окну на мобильном устройстве или выбрав меню с пульта телевизора).
Если в приложении есть активный сеанс мультимедиа , появятся элементы управления «Воспроизведение», «Пауза», «Следующий» и «Предыдущий».
Вы также можете явно указать пользовательские действия, создав PictureInPictureParams
с помощью PictureInPictureParams.Builder.setActions()
перед входом в режим PiP, и передать параметры при входе в режим PiP с помощью enterPictureInPictureMode(android.app.PictureInPictureParams)
или setPictureInPictureParams(android.app.PictureInPictureParams)
. Будь осторожен. Если вы попытаетесь добавить больше, чем getMaxNumPictureInPictureActions()
, вы получите только максимальное число.
Продолжение воспроизведения видео в режиме PiP
Когда ваше действие переключается на PiP, система переводит действие в состояние паузы и вызывает метод onPause()
действия. Воспроизведение видео не следует приостанавливать, а продолжать воспроизведение, если действие приостановлено при переходе в режим «картинка в картинке».
В 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 может быть отключен на устройствах с небольшим объемом оперативной памяти. Прежде чем ваше приложение будет использовать PiP, убедитесь, что оно доступно, вызвав hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE)
.
PiP предназначен для действий, при которых воспроизводится полноэкранное видео. При переключении активности в режим PiP избегайте показа чего-либо, кроме видеоконтента. Отслеживайте, когда ваше действие переходит в режим PiP, и скрывайте элементы пользовательского интерфейса, как описано в разделе «Обработка пользовательского интерфейса во время PiP» .
Когда действие находится в режиме PiP, по умолчанию оно не получает фокус ввода. Чтобы получать события ввода в режиме PiP, используйте MediaSession.setCallback()
. Дополнительную информацию об использовании setCallback()
см. в разделе «Отображение карты «Сейчас играет» .
Когда ваше приложение находится в режиме PiP, воспроизведение видео в окне PiP может вызвать помехи звуку в другом приложении, например музыкальном проигрывателе или приложении голосового поиска. Чтобы избежать этого, запрашивайте фокусировку звука при начале воспроизведения видео и обрабатывайте уведомления об изменении фокусировки звука, как описано в разделе «Управление фокусировкой звука» . Если вы получили уведомление о потере фокуса звука в режиме «картинка в картинке», приостановите или остановите воспроизведение видео.
Когда ваше приложение собирается перейти в режим «картинка в картинке», обратите внимание, что только самое верхнее действие входит в режим «картинка в картинке». В некоторых ситуациях, например, на многооконных устройствах, возможно, что действие, указанное ниже, теперь будет отображаться и снова станет видимым вместе с действием PiP. Вам следует обработать этот случай соответствующим образом, включая приведенное ниже действие по получению обратного вызова onResume()
или onPause()
. Также возможно, что пользователь может взаимодействовать с действием. Например, если у вас отображается действие списка видео и действие воспроизведения видео в режиме PiP, пользователь может выбрать новое видео из списка, и действие PiP должно обновиться соответствующим образом.
Дополнительный пример кода
Чтобы загрузить пример приложения, написанного на Kotlin, см. раздел «Пример Android PictureInPicture (Kotlin)» .