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