Библиотека Jetpack для режима "Картинка в картинке" – это простое и надежное решение, которое позволяет разработчикам приложений Android реализовать режим "Картинка в картинке", особенно в приложениях для воспроизведения медиаконтента, видеосвязи и навигации. Благодаря единому API библиотека помогает избавиться от шаблонного кода и распространенных ошибок в приложении, а также повысить качество работы режима "картинка в картинке".
Библиотека Jetpack для режима "картинка в картинке" упрощает работу с существующими API, устраняя ряд ключевых проблем и несоответствий в экосистеме Android:
- Фрагментация ОС. Библиотека автоматически обрабатывает различия в вызовах API для режима "Картинка в картинке" в разных версиях Android. Например, до Android 12 используется
enterPictureInPictureMode, а после –isAutoEnterEnabled. Разработчикам не нужно учитывать различия между версиями. - Неправильные параметры режима "Картинка в картинке". API предоставляет единое решение для правильной настройки параметров режима "Картинка в картинке", например
setSourceRectHint, чтобы создавать плавные и качественные анимации во время воспроизведения медиаконтента. - Унифицированные обратные вызовы состояния картинки в картинке.
onPictureInPictureModeChangedиonPictureInPictureUiStateChangedобъединены в единый интерфейс обратного вызова (PictureInPictureDelegate.OnPictureInPictureEventListener), что упрощает управление состоянием и интерфейсом. - Меньше шаблонного кода. Библиотека позволяет сократить количество повторяющегося шаблонного кода, предлагая готовые наборы
RemoteActionsдля распространенных сценариев, таких как управление воспроизведением и действия во время видеовызовов. - Перспективы. Дополнительные функции режима "Картинка в картинке" предоставляются через библиотеку Jetpack, что позволяет разработчикам получать доступ к дополнительным возможностям с минимальными усилиями или без них.
Процесс переноса
Определите категорию сценария использования приложения и устаревшую логику режима "Картинка в картинке":
Категории: "Воспроизведение видео", "Навигация" или "Видеовызов".
Устаревшая логика определения режима "картинка в картинке":
onUserLeaveHintsetAutoEnterEnabledonPictureInPictureModeChangedonPictureInPictureUiStateChangedsetPictureInPictureParams.
2. Конфигурация AndroidManifest
Убедитесь, что в файле AndroidManifest.xml для Activity, которое переходит в режим "Картинка в картинке", указана поддержка необходимых изменений конфигурации (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. Плавное изменение размера обычно не поддерживается, и подсказка sourceRectHint не требуется. - Воспроизведение видео.
VideoPlaybackPictureInPictureавтоматически отслеживает границы проигрывателя для подсказки исходного прямоугольника и по умолчанию позволяет плавно изменять размер.
Чтобы перейти на библиотеку Jetpack, замените существующую реализацию режима "Картинка в картинке" на API библиотеки Jetpack. Сложность и стоимость внедрения будут зависеть от текущей реализации приложения.
В следующих разделах описаны некоторые типичные сценарии использования режима "Картинка в картинке" и необходимые шаги по его реализации.
Навигация
Приложение сообщает библиотеке о том, активно ли состояние навигации, и задает соотношение сторон. Остальное сделает библиотека Jetpack.
Основные различия
- В приложении не нужно различать автоматический и обычный вход.
- Объединенные интерфейсы обратного вызова.
- Новый конструктор
PictureInPictureParamsдля обратной совместимости.
Видеовызов
Приложение сообщает библиотеке о том, активен ли вызов, и задает соотношение сторон.
Основные различия
- В приложении не нужно различать автоматический и обычный вход.
- Объединенные интерфейсы обратного вызова.
- Новый конструктор
PictureInPictureParamsдля обратной совместимости. - Стандартизированные значки действий для видеовызова.
5. Перенос кода
- Логика входа. Замените логику, относящуюся к API, например
setAutoEnterEnabledдля Android 12 и более поздних версий илиonUserLeaveHintдля Android 11 и более ранних версий, наsetEnabled. Запускайте его каждый раз, когда меняется статус соответствия требованиям для режима "Картинка в картинке". - Обратные вызовы. Объедините
onPictureInPictureModeChanged(переключение макета) иonPictureInPictureUiStateChanged(анимация/состояния) в единый обратный вызов на основе событийonPictureInPictureEvent. - Действия и параметры. Обновляйте параметры с помощью
setActionsиsetAspectRatioв экземпляре шаблона при каждом изменении.
Шаблоны реализации
Примеры реализации.
Навигация и видеовызовы
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() } } } }