Добавляйте видео с помощью функции «картинка в картинке» (PiP)

Начиная с 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 в своем приложении, зарегистрируйте свою видеоактивность в манифесте, установив 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 . И это дает дополнительное преимущество, поскольку обеспечивает гораздо более плавные переходы. Подробные сведения см. в разделе Как сделать переход в режим «картинка в картинке» более плавным с помощью навигации с помощью жестов .

Если вы ориентируетесь на 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 переключаются в режим 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 обеспечивает гораздо более плавную анимацию при переходе к видеоконтенту в режиме «картинка в картинке» с помощью навигации по жестам — например, при пролистывании вверх из полноэкранного режима.

Выполните следующие шаги, чтобы внести это изменение, и обратитесь к этому образцу для справки:

  1. Используйте setAutoEnterEnabled для создания PictureInPictureParams.Builder :

    Котлин

    setPictureInPictureParams(PictureInPictureParams.Builder()
        .setAspectRatio(aspectRatio)
        .setSourceRectHint(sourceRectHint)
        .setAutoEnterEnabled(true)
        .build())

    Ява

    setPictureInPictureParams(new PictureInPictureParams.Builder()
        .setAspectRatio(aspectRatio)
        .setSourceRectHint(sourceRectHint)
        .setAutoEnterEnabled(true)
        .build());
  2. Вызовите setPictureInPictureParams с обновленным PictureInPictureParams заранее. Приложение не ожидает обратного вызова onUserLeaveHint (как это было бы в Android 11).

    Например, вы можете вызвать setPictureInPictureParams при самом первом воспроизведении и при любом последующем воспроизведении, если соотношение сторон изменено.

  3. Вызовите setAutoEnterEnabled(false) , но только по мере необходимости. Например, вы, вероятно, не захотите вводить PiP, если текущее воспроизведение находится в состоянии паузы.

Установите правильный sourceRectHint для входа в режим PiP и выхода из него.

Начиная с появления PiP в Android 8.0, setSourceRectHint указывал область действия, которая видна после перехода в режим «картинка в картинке» — например, границы просмотра видео в видеоплеере.

В Android 12 система использует sourceRectHint для реализации более плавной анимации как при входе в режим PiP, так и при выходе из него.

Чтобы правильно настроить sourceRectHint для входа и выхода из режима PiP:

  1. Создайте 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);
  2. При необходимости обновите 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)» .

,

Начиная с 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 Maps переключается в режим 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 обеспечивает гораздо более плавную анимацию при переходе к видеоконтенту в режиме «картинка в картинке» с использованием навигации с помощью жестов — например, при пролистывании до дома из полноэкранного режима.

Выполните следующие шаги, чтобы внести это изменение, и обратитесь к этому образцу для справки:

  1. Используйте setAutoEnterEnabled для создания PictureInPictureParams.Builder :

    Котлин

    setPictureInPictureParams(PictureInPictureParams.Builder()
        .setAspectRatio(aspectRatio)
        .setSourceRectHint(sourceRectHint)
        .setAutoEnterEnabled(true)
        .build())

    Ява

    setPictureInPictureParams(new PictureInPictureParams.Builder()
        .setAspectRatio(aspectRatio)
        .setSourceRectHint(sourceRectHint)
        .setAutoEnterEnabled(true)
        .build());
  2. Вызовите setPictureInPictureParams с обновленным PictureInPictureParams заранее. Приложение не ожидает обратного вызова onUserLeaveHint (как это было бы в Android 11).

    Например, вы можете вызвать setPictureInPictureParams при самом первом воспроизведении и при любом последующем воспроизведении, если соотношение сторон изменено.

  3. Вызовите setAutoEnterEnabled(false) , но только по мере необходимости. Например, вы, вероятно, не захотите вводить PiP, если текущее воспроизведение находится в состоянии паузы.

Установите правильный sourceRectHint для входа в режим PiP и выхода из него.

Начиная с появления PiP в Android 8.0, setSourceRectHint указывал область действия, которая видна после перехода в режим «картинка в картинке» — например, границы просмотра видео в видеоплеере.

В Android 12 система использует sourceRectHint для реализации более плавной анимации как при входе в режим PiP, так и при выходе из него.

Чтобы правильно настроить sourceRectHint для входа и выхода из режима PiP:

  1. Создайте 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);
  2. При необходимости обновите 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)» .

,

Начиная с 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, используя Pinch-to-Zoom.

Ваше приложение контролирует, когда текущая деятельность входит в режим 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, вы можете переключить свою активность в режим PIP, установив флаг setAutoEnterEnabled на true . С помощью этой настройки активность автоматически переключается на режим 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, если пользователь нажимает кнопку «Домашний или Recest», пока приложение навигации. Вы можете поймать этот случай, переопределив onUserLeaveHint() :

Котлин

override fun onUserLeaveHint() {
    if (iWantToBeInPipModeNow()) {
        enterPictureInPictureMode()
    }
}

Ява

@Override
public void onUserLeaveHint () {
    if (iWantToBeInPipModeNow()) {
        enterPictureInPictureMode();
    }
}

Рекомендуется: предоставьте пользователям опыт перехода PIP

Android 12 добавил значительные косметические улучшения к анимированным переходам между полноэкранными и PIP Windows. Мы настоятельно рекомендуем реализовать все применимые изменения; После того, как вы это сделаете, эти изменения автоматически масштабируются на больших экранах, таких как складные и планшеты, без какой -либо дополнительной необходимой работы.

Если ваше приложение не включает применимые обновления, переходы PIP все еще функциональны, но анимации менее отполированы. Например, переход от полноэкранного режима в PIP может привести к исчезновению окна PIP во время перехода, прежде чем он появится, когда переход будет завершен.

Эти изменения включают следующее.

  • Переход к режиму PIP с более плавным от навигации по жестам
  • Установка надлежащей sourceRectHint для входа и выхода из режима PIP
  • Отключение бесшовного изменения размера для содержания не-видео

Обратитесь к образцу Android Kotlin PictureInpicture в качестве ссылки для обеспечения полированного перехода.

Сделайте переходы в режим PIP более плавным от навигации по жестам

Начиная с Android 12, флаг setAutoEnterEnabled обеспечивает гораздо более плавную анимацию для перехода на видеоконтент в режиме PIP, используя навигацию по жестам, например, при перемещении домой из полноэкранного.

Заполните следующие шаги, чтобы внести это изменение и обратитесь к этому образцу для ссылки:

  1. Используйте setAutoEnterEnabled для создания PictureInPictureParams.Builder :

    Котлин

    setPictureInPictureParams(PictureInPictureParams.Builder()
        .setAspectRatio(aspectRatio)
        .setSourceRectHint(sourceRectHint)
        .setAutoEnterEnabled(true)
        .build())

    Ява

    setPictureInPictureParams(new PictureInPictureParams.Builder()
        .setAspectRatio(aspectRatio)
        .setSourceRectHint(sourceRectHint)
        .setAutoEnterEnabled(true)
        .build());
  2. Вызовите setPictureInPictureParams с актуальной PictureInPictureParams рано. Приложение не ждет обратного вызова onUserLeaveHint (как это было бы в Android 11).

    Например, вы можете позвонить setPictureInPictureParams в самом первом воспроизведении и любом следующем воспроизведении, если изменено соотношение сторон.

  3. Вызовите setAutoEnterEnabled(false) , но только так, как это необходимо. Например, вы, вероятно, не хотите входить в PIP, если текущее воспроизведение в состоянии приостановки.

Установите надлежащий sourceRectHint для входа и выхода из режима PIP

Начиная с введения PIP в Android 8.0, setSourceRectHint указывала на область деятельности, которая видно после перехода в картинку в картинке-например, границы представления видео в видеоплеере.

С Android 12 система использует sourceRectHint для реализации гораздо более плавной анимации как при входе, так и при выходе из режима PIP.

Чтобы правильно установить sourceRectHint для входа и выхода из режима PIP:

  1. Построить 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);
  2. При необходимости обновите 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());
        }
    });

Отключить бесшовное изменение размера для содержания не-видео

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, например, представления или макет, которые включают такую ​​информацию, как предложения, предстоящее видео, рейтинги и названия. Когда приложение переходит в режим 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 и восстановите их, когда ваша деятельность снова станет полным экраном.

Добавьте элементы управления

Окно 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 или выходит из 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, сделайте паузу или остановите воспроизведение видео.

Когда ваше приложение собирается войти в PIP, обратите внимание на то, что только высшая деятельность входит в картинку. В некоторых ситуациях, таких как на нескольких Window устройствах, возможно, активность, ниже ниже, теперь будет показана и снова станет видна вместе с активностью PIP. Вы должны соответствующим образом рассмотреть этот случай, в том числе приведенное ниже действие, получая обратное вызов onResume() или обратный вызов onPause() . Также возможно, что пользователь может взаимодействовать с деятельностью. Например, если у вас отображается деятельность списка видео, и воспроизводимая видео активность в режиме PIP, пользователь может выбрать новое видео из списка, и активность PIP должна обновляться соответствующим образом.

Дополнительный пример кода

Чтобы загрузить образец приложения, написанного в Kotlin, см. Пример Android PictureInpicture (Kotlin) .