Библиотека Jetpack для режима «картинка в картинке» (PiP) предлагает оптимизированное и надежное решение для разработчиков Android-приложений, позволяющее реализовать функциональность PiP, особенно для воспроизведения мультимедиа, видеосвязи и навигации. Предоставляя единый API, библиотека помогает устранить шаблонный код, распространенные ошибки в приложениях и улучшить общее качество пользовательского опыта в режиме PiP.
Библиотека PiP Jetpack упрощает работу с существующими API PiP, устраняя ряд ключевых проблем и несоответствий в экосистеме Android:
- Фрагментация ОС : Библиотека автоматически обрабатывает различия в вызовах API PiP в разных версиях Android, например, использует
enterPictureInPictureModeдо Android 12 иisAutoEnterEnabledпосле, поэтому разработчикам не нужно управлять различиями в версиях. - Неправильные параметры PiP : Предоставляется унифицированное решение для правильной настройки параметров PiP, например,
setSourceRectHint, для создания плавной и высококачественной анимации во время воспроизведения мультимедиа. - Единый интерфейс обратного вызова для управления состоянием PiP : он объединяет события
onPictureInPictureModeChangedиonPictureInPictureUiStateChangedв единый интерфейс обратного вызова (PictureInPictureDelegate.OnPictureInPictureEventListener) для упрощения управления состоянием и пользовательским интерфейсом. - Сокращение количества повторяющегося шаблонного кода : библиотека уменьшает объем повторяющегося шаблонного кода, предлагая предопределенные наборы
RemoteActionsдля распространенных сценариев использования, таких как управление воспроизведением и действия при видеозвонках. - Перспективная реализация : Дополнительные функции PiP (картинка в картинке) предоставляются через библиотеку Jetpack, что позволяет пользователям получать доступ к дополнительной функциональности с минимальными усилиями или без них.
Процесс миграции
Определите категорию использования приложения и устаревшую логику режима «картинка в картинке»:
Категории: Воспроизведение видео, Навигация или Видеозвонок.
Необходимо идентифицировать устаревшую логику PiP:
-
onUserLeaveHint -
setAutoEnterEnabled -
onPictureInPictureModeChanged -
onPictureInPictureUiStateChanged -
setPictureInPictureParams.
2. Конфигурация AndroidManifest
Убедитесь, что Activity, входящая в режим PiP, указывает поддержку этой функции в файле AndroidManifest.xml с необходимыми configChanges , чтобы предотвратить ненужные перезапуски:
<activity
android:name="VideoActivity" android:supportsPictureInPicture="true"
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation">
</activity>
3. Настройка среды
Добавьте необходимые зависимости в build.gradle :
dependencies {
implementation("androidx.core:core:1.18.0")
implementation("androidx.activity:activity:1.13.0")
implementation("androidx.core:core-pip:1.0.0-alpha02") }
Для зависимостей используйте последние версии библиотек AndroidX, подробную информацию см. на странице релизов .
4. Выбор и инициализация шаблона
Выберите шаблон реализации, который наилучшим образом соответствует сценарию использования приложения:
- Навигация и видеозвонок :
BasicPictureInPicture; плавное изменение размера обычно не поддерживается, и вам не нужна подсказка для исходного прямоугольника. - Воспроизведение видео :
VideoPlaybackPictureInPicture; автоматически отслеживает границы области просмотра проигрывателя для подсказки исходного прямоугольника и по умолчанию включает плавное изменение размера.
Для внедрения библиотеки Jetpack замените существующую пользовательскую реализацию режима «картинка в картинке» на API библиотеки Jetpack. Сложность и стоимость внедрения будут варьироваться в зависимости от текущей реализации приложения.
В следующих разделах описаны некоторые типичные сценарии использования PiP и необходимые этапы реализации:
Навигация
Приложение сообщает библиотеке об активном или неактивном состоянии навигации и устанавливает соотношение сторон. Библиотека Jetpack обрабатывает остальное.
Ключевые отличия:
- Нет необходимости различать автоматический ввод и ввод с помощью устаревших функций на стороне приложения.
- Объединенные интерфейсы обратного вызова.
- Новый конструктор
PictureInPictureParamsдля обеспечения обратной совместимости.
Видеозвонок
Приложение сообщает библиотеке об активном или неактивном состоянии вызова и устанавливает соотношение сторон.
Ключевые отличия:
- Нет необходимости различать автоматический ввод и ввод с помощью устаревших функций на стороне приложения.
- Объединенные интерфейсы обратного вызова.
- Новый конструктор
PictureInPictureParamsдля обеспечения обратной совместимости. - Стандартизированные значки действий для видеозвонка.
5. Миграция кода
- Логика входа: Замените специфичную для API логику, такую как
setAutoEnterEnabledдля Android 12 и выше илиonUserLeaveHintдля Android 11 и ниже, наsetEnabled. Запускайте это всякий раз, когда изменяется статус доступности режима «картинка в картинке». - Обратные вызовы: Объединить
onPictureInPictureModeChanged(переключение макета) иonPictureInPictureUiStateChanged(анимация/состояния) в единый обработчик событийonPictureInPictureEvent. - Действия и параметры: Обновляйте параметры с помощью
setActionsиsetAspectRatioв экземпляре шаблона всякий раз, когда они изменяются. - Специальная обработка видео: Для видеоприложений используйте
setPlayerViewдля автоматического обновления подсказок исходного прямоугольника и обеспечения плавных переходов. ` ### 6. Очистка
Для VideoPlaybackPictureInPicture вызовите метод close в onDispose или onDestroy , чтобы освободить ресурсы, такие как трекеры просмотра.
Эталонные шаблоны реализации
Примеры реализаций.
Навигация и видеозвонок
class NavOrVideoCallJpipActivity : ComponentActivity(), PictureInPictureDelegate.OnPictureInPictureEventListener { private lateinit var pictureInPictureImpl: BasicPictureInPicture override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) pictureInPictureImpl = BasicPictureInPicture(this) // BasicPictureInPicture is ideal for Navigation and Video call use cases. pictureInPictureImpl.addOnPictureInPictureEventListener( ContextCompat.getMainExecutor(this), this ) setContent { } } override fun onPictureInPictureEvent( event: PictureInPictureDelegate.Event, config: Configuration? ) { when (event) { PictureInPictureDelegate.Event.ENTERED -> { /* Toggle to PiP layout */ } PictureInPictureDelegate.Event.EXITED -> { /* Toggle to Full-screen layout */ } PictureInPictureDelegate.Event.STASHED -> { /* Optional: PiP is stashed */ } PictureInPictureDelegate.Event.UNSTASHED -> { /* Optional: PiP is unstashed */ } } } }
Воспроизведение видео
class VideoPlaybackJpipActivity : ComponentActivity(), PictureInPictureDelegate.OnPictureInPictureEventListener { private lateinit var pictureInPictureImpl: VideoPlaybackPictureInPicture override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) pictureInPictureImpl = VideoPlaybackPictureInPicture(this) pictureInPictureImpl.addOnPictureInPictureEventListener( ContextCompat.getMainExecutor(this), this ) setContent { ContentScreen(pictureInPictureImpl) } } override fun onPictureInPictureEvent( event: PictureInPictureDelegate.Event, config: Configuration? ) { when (event) { PictureInPictureDelegate.Event.ENTER_ANIMATION_START -> { /* Hide overlays */ } PictureInPictureDelegate.Event.ENTER_ANIMATION_END -> { /* Animation finished */ } PictureInPictureDelegate.Event.ENTERED -> { /* Switch to PiP layout */ } PictureInPictureDelegate.Event.STASHED -> { /* PiP stashed */ } PictureInPictureDelegate.Event.UNSTASHED -> { /* PiP unstashed */ } PictureInPictureDelegate.Event.EXITED -> { /* Return to full-screen */ } } } @Composable fun ContentScreen(pipController: VideoPlaybackPictureInPicture) { DisposableEffect(pipController) { onDispose { pipController.close() } } } }