Реализация Multi-Window для Android-приложения
Multi-Window — режим разделённого экрана на Android 7.0+ (API 24) и Picture-in-Picture для Android 8.0+ (API 26). С Android 12 режим разделённого экрана включён по умолчанию для всех приложений, если явно не отключён через android:resizeableActivity="false". Это значит: приложение уже может оказаться в split-screen у пользователей, и если оно к этому не готово — UI ломается.
Что значит «готово к Multi-Window»
Приложение, работающее в разделённом экране, получает изменённые размеры окна несколько раз за секунду при изменении разделителя. Каждое такое изменение — onConfigurationChanged() или пересоздание Activity (если android:configChanges не указан). Если Activity пересоздаётся при каждом движении разделителя — пользователь видит мигание и потерю состояния.
Правильный подход: объявить в манифесте, что Activity сама обрабатывает изменения конфигурации:
<activity
android:name=".MainActivity"
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
android:resizeableActivity="true" />
И в onConfigurationChanged() пересчитывать layout без пересоздания — через WindowMetrics API (API 30+) или DisplayMetrics для более старых версий.
Адаптивный UI. С Jetpack Compose: BoxWithConstraints или WindowSizeClass из androidx.window:window для определения текущего класса окна (Compact, Medium, Expanded). Не хардкодить «планшетный layout» по isTablet() — проверять реальный размер доступного окна, который в split-screen на планшете может быть как на телефоне.
Picture-in-Picture
PiP работает через enterPictureInPictureMode(PictureInPictureParams). Типичный сценарий — видеоплеер. При переходе в PiP:
val params = PictureInPictureParams.Builder()
.setAspectRatio(Rational(16, 9))
.setActions(listOf(
RemoteAction(/* пауза/воспроизведение */),
RemoteAction(/* следующий трек */)
))
.build()
enterPictureInPictureMode(params)
RemoteAction — кнопки, видимые в PiP-окне. Нажатие отправляет PendingIntent — обычно BroadcastReceiver внутри Activity.
Проблема: onStop() вызывается при переходе в PiP. Если видеоплеер останавливается в onStop() — воспроизведение прерывается. Решение: проверять isInPictureInPictureMode в onStop() и не останавливать плеер.
override fun onStop() {
super.onStop()
if (!isInPictureInPictureMode) {
player.pause()
}
}
onPictureInPictureModeChanged() — колбэк для обновления UI: в PiP-режиме скрывать кнопки управления, оставлять только видеоконтент.
Drag and Drop между окнами
С API 24 поддерживается Drag and Drop между приложениями в split-screen. Для приёма: View.setOnDragListener() с обработкой DragEvent.ACTION_DROP. Для инициирования: View.startDragAndDrop() с ClipData. С API 33 появился улучшенный DropHelper из androidx.draganddrop — упрощает обработку различных MIME-типов.
Freeform-режим
На ChromeOS и некоторых Android-устройствах приложения работают в freeform-режиме — произвольный размер окна. android:resizeableActivity="true" + корректная обработка onConfigurationChanged — необходимое условие. Тестирование: Developer Options → Force desktop mode (на поддерживаемых устройствах) или эмулятор ChromeOS.
WindowManager library (androidx.window:window:1.3.x) предоставляет WindowInfoTracker с Flow<WindowLayoutInfo> — для складных устройств (Fold, Flip) позволяет реагировать на состояние шарнира (half-opened, tabletop mode). Для приложений, которые должны поддерживать Galaxy Z Fold — без этой библиотеки корректная адаптация невозможна.
Реализация полной поддержки Multi-Window с PiP, адаптивным UI и тестированием: 3-5 дней. Стоимость рассчитывается индивидуально после оценки текущего состояния UI-кода.







