Jetpack Window Manager для складных устройств

В решении Jetpack Window Manager предоставляется стандартный API для работы со всеми складными устройствами. Он предусматривает два важных класса:

  • DisplayFeature — обнаруживает разрывы в непрерывной плоской поверхности экрана, например петли или складки. WindowManager вернет коллекцию функций отображения при обратном вызове изменения макета.
  • FoldingFeature — предоставляет сведения о конкретной функции устройства. В Surface Duo есть только одна функция складывания, но у других устройств их может быть больше.

Аналогичное руководство можно найти в практической работе по складным устройствам Android. Узнайте больше о разработке решений для складных устройств в документации по Android. Кроме того, на сайте GitHub доступны примеры от команды Android. В заметках о выпуске Jetpack фиксируются изменения в Window Manager по мере обновления.

Совет

Элементы управления и вспомогательные классы в библиотеке для двухэкранных устройств Surface Duo работают с Window Manager. Выполните инструкции, чтобы добавить нужные пакеты в свой проект приложения.

Чтобы использовать Window Manager непосредственно в коде, выполните приведенные ниже инструкции.

Добавление зависимостей

  1. Убедитесь, что у вас есть репозиторий mavenCentral() в файле build.gradle верхнего уровня:

    allprojects {
        repositories {
            google()
            mavenCentral()
         }
    }
    
  2. Убедитесь, что для compileSdkVersion и targetSdkVersion выбрана версия API 31 или выше в файле build.gradle на уровне модуля:

    android { 
        compileSdkVersion 31
    
        defaultConfig { 
            targetSdkVersion 31
        } 
        ... 
    }
    
    
  3. Добавьте в файл уровня модуля build.gradle следующие зависимости:

    dependencies {
        implementation "androidx.window:window:1.0.0"
        implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.0'
    }
    

Использование диспетчера окон в коде Kotlin

При доступе к свойствам Window Manager в проектах Kotlin важно настроить правильный поток информации. В противном случае вы можете получать слишком мало или слишком много обновлений событий, что может повлиять на производительность приложения.

Чтобы инициализировать и использовать объект WindowInfoTracker, выполните следующие действия:

  1. В классе MainActivity создайте переменную для WindowInfoTracker. Убедитесь, что в верхней части файла добавлена инструкция import androidx.window.layout.WindowInfoTracker.

    class MainActivity : AppCompatActivity() {
        private lateinit var windowInfoTracker: WindowInfoTracker
    
  2. Инициализируйте WindowInfoTracker в методе действия onCreate и настройте поток для сбора сведений из свойства windowLayoutInfo.

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    
        // Initialize the window manager
        windowInfoTracker = WindowInfoTracker.getOrCreate(this@MainActivity)
    
        // Set up a flow
        lifecycleScope.launch(Dispatchers.Main) {
            lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
                windowInfoTracker.windowLayoutInfo(this@MainActivity)
                    .collect { 
                        // Check FoldingFeature properties here
                    }
            }
        }
    }
    

    Убедитесь, что в верхней части файла также добавлены следующие импортируемые компоненты:

    import androidx.lifecycle.Lifecycle
    import androidx.lifecycle.lifecycleScope
    import androidx.lifecycle.repeatOnLifecycle
    import kotlinx.coroutines.Dispatchers
    import kotlinx.coroutines.flow.collect
    import kotlinx.coroutines.launch
    
  3. Добавьте код, чтобы проверить поток WindowLayoutInfo на наличие свойств функций складывания. При выполнении этого кода действие будет обновлено с учетом текущего положения устройства и функций отображения (если приложение развертывается поверх сгиба или петли).

    В приведенном ниже фрагменте кода действие отображает другой текст на основе свойств FoldingFeature.

    В этом примере используется элемент TextView, называемый layout_change_text, который показывает тип загораживания и значение isSeparating для всех обнаруженных функций складывания.

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    
        windowInfoTracker = WindowInfoTracker.getOrCreate(this@MainActivity)
    
        lifecycleScope.launch(Dispatchers.Main) {
            lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
                windowInfoTracker.windowLayoutInfo(this@MainActivity)
                    .collect { newLayoutInfo ->
                        layout_change_text.text = "No display features detected"
                        for (displayFeature : DisplayFeature in newLayoutInfo.displayFeatures) {
                            if (displayFeature is FoldingFeature && displayFeature.occlusionType == FoldingFeature.OcclusionType.NONE) {
                                layout_change_text.text = "App is spanned across a fold, " +
                                    "isSeparating = ${displayFeature.isSeparating}"
                            }
                            if (displayFeature is FoldingFeature && displayFeature.occlusionType == FoldingFeature.OcclusionType.FULL) {
                                layout_change_text.text = "App is spanned across a hinge, " +
                                    "isSeparating = ${displayFeature.isSeparating}"
                            }
                        }
                    }
            }
        }
    }
    

Свойства функций складывания

Класс WindowLayoutInfo содержит коллекцию элементов DisplayFeature, один или несколько из которых могут быть экземплярами класса FoldingFeature.

Функции складывания имеют следующие свойства:

  • bounds — координаты ограничивающего прямоугольника функции складывания;
  • occlusionType — если функция складывания скрывает содержимое (FULL или NONE);
  • orientation — ориентация функции складывания (HORIZONTAL или VERTICAL);
  • state — угол функции складывания (HALF_OPENED или FLAT);
  • isSeparating — если функция складывания разделяет область отображения на две отдельные части.

Вы можете запросить эти свойства, чтобы принять решение о том, как настроить макет после изменения конфигурации.

isSeparating

При выборе места для элементов управления или количества отображаемых областей содержимого используйте свойство isSeparating. Благодаря этому полю ваше приложение обеспечит оптимальное взаимодействие с пользователем на всех складных устройствах:

  • Для устройств с двумя экранами значение всегда будет true, если приложение развертывается поверх петли.
  • Для других складных устройств значение будет true только в состоянии HALF_OPENED, например, если устройство стоит на столе.

Используйте свойство isSeparating, чтобы решить, следует ли адаптировать макет пользовательского интерфейса приложения для складного устройства или использовать пользовательский интерфейс по умолчанию, если разделение отсутствует:

private fun updateCurrentLayout(newLayoutInfo: WindowLayoutInfo) {
   for (displayFeature in newLayoutInfo.displayFeatures) {
       val foldFeature = displayFeature as? FoldingFeature
       foldFeature?.let {
           if (it.isSeparating) {
               // The content is separated by the FoldingFeature.
               // Here is where you should adapt your UI.
           } else {
               // The content is not separated.
               // Users can see and interact with your UI properly.
           }
       }
   }
}

Ознакомьтесь с более подробным примером использования поля isSeparating.

Google также предоставляет документацию и примеры, связанные с этим свойством, в рамках своего руководства по складным устройствам и устройствам с большим экраном.

Примеры

Репозиторий surface-duo-jetpack-window-manager-samples на GitHub содержит несколько примеров Kotlin, демонстрирующих разные интерфейсы с поддержкой двух экранов, созданных с использованием Jetpack Window Manager и традиционной системы просмотра.

В репозитории surface-duo-compose-samples на GitHub также есть примеры Kotlin для устройств с двумя экранами, в которых используется Jetpack Window Manager, но в этих примерах пользовательский интерфейс построен с помощью Jetpack Compose.

API Java

Сведения о том, как получить доступ к классу WindowInfoTracker через WindowInfoTrackerCallbackAdapter, см. в записи блога, посвященной использованию Jetpack Window Manager с Java и этом примере Java.

Ресурсы