Ограничить ориентацию приложения на телефонах, но не на устройствах с большим экраном,Ограничить ориентацию приложения на телефонах, но не на устройствах с большим экраном

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

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

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

Результаты

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

Совместимость версий

Данная реализация совместима со всеми уровнями API.

Зависимости

Котлин

implementation("androidx.window:window:1.5.1")
implementation("androidx.window:window-core:1.5.1")

Классный

implementation "androidx.window:window:1.5.1"
implementation "androidx.window:window-core:1.5.1"

Управление ориентацией приложения

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

1. Укажите параметры ориентации в манифесте приложения.

Вы можете либо не указывать элемент screenOrientation в манифесте приложения (в этом случае ориентация по умолчанию будет unspecified ), либо установить ориентацию экрана в fullUser . Если пользователь не заблокировал поворот экрана на основе данных датчиков, ваше приложение будет поддерживать все ориентации устройства.

<activity
    android:name=".MyActivity"
    android:screenOrientation="fullUser">

Разница между unspecified и fullUser невелика, но важна. Если вы не указываете значение screenOrientation , система выбирает ориентацию самостоятельно, и политика, используемая системой для определения ориентации, может отличаться на разных устройствах.

С другой стороны, указание fullUser более точно соответствует поведению, заданному пользователем для устройства: если пользователь заблокировал поворот экрана на основе данных с датчиков, приложение следует предпочтениям пользователя; в противном случае система допускает любую из четырех возможных ориентаций экрана (портретная, альбомная, обратная портретная или обратная альбомная).

Кроме того, вы можете использовать nosensor для определения ориентации без учета данных датчика, но следующий код будет работать аналогичным образом. См. screenOrientation .

2. Определите размер экрана.

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

Добавьте библиотеки Jetpack WindowManager в файл build.gradle или build.gradle.kts модуля:

Котлин

implementation("androidx.window:window:version")
implementation("androidx.window:window-core:version")

Классный

implementation 'androidx.window:window:version'
implementation 'androidx.window:window-core:version'

Используйте метод ` WindowMetricsCalculator#computeMaximumWindowMetrics() ` из Jetpack WindowManager, чтобы получить размер экрана устройства в виде объекта WindowMetrics . Метрики окна можно сравнить с классами размеров окна, чтобы решить, когда следует ограничить ориентацию.

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

Используйте контрольные точки WindowSizeClass#minWidthDp и WindowSizeClass#minHeightDp для определения размера экрана:

/** Determines whether the device has a compact screen. **/
fun compactScreen() : Boolean {
    val metrics = WindowMetricsCalculator.getOrCreate().computeMaximumWindowMetrics(this)
    val width = metrics.bounds.width()
    val height = metrics.bounds.height()
    val density = resources.displayMetrics.density
    val windowSizeClass =
        BREAKPOINTS_V1.computeWindowSizeClass(width / density, height / density)
    return windowSizeClass.minWidthDp == 0
}
    Примечание:
  • Примеры реализованы как методы активности; поэтому активность разыменовывается как this в аргументе функции computeMaximumWindowMetrics() .
  • Метод computeMaximumWindowMetrics() используется вместо computeCurrentWindowMetrics() , потому что приложение может быть запущено в многооконном режиме, который игнорирует настройку ориентации экрана. Нет смысла определять размер окна приложения и переопределять настройку ориентации, если окно приложения не занимает весь экран устройства.

Инструкции по объявлению зависимостей для обеспечения доступности метода computeMaximumWindowMetrics() в вашем приложении см. в документации WindowManager.

3. Переопределение настроек манифеста приложения

Если вы определили, что устройство имеет компактный размер экрана, вы можете вызвать Activity#setRequestedOrientation() чтобы переопределить параметр screenOrientation указанный в манифесте:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    requestedOrientation = if (compactScreen())
        ActivityInfo.SCREEN_ORIENTATION_PORTRAIT else
        ActivityInfo.SCREEN_ORIENTATION_FULL_USER
    ...
    // Replace with a known container that you can safely add a
    // view to where the view won't affect the layout and the view
    // won't be replaced.
    val container: ViewGroup = binding.container

    // Add a utility view to the container to hook into
    // View.onConfigurationChanged. This is required for all
    // activities, even those that don't handle configuration
    // changes. You can't use Activity.onConfigurationChanged,
    // since there are situations where that won't be called when
    // the configuration changes. View.onConfigurationChanged is
    // called in those scenarios.
    container.addView(object : View(this) {
        override fun onConfigurationChanged(newConfig: Configuration?) {
            super.onConfigurationChanged(newConfig)
            requestedOrientation = if (compactScreen())
                ActivityInfo.SCREEN_ORIENTATION_PORTRAIT else
                ActivityInfo.SCREEN_ORIENTATION_FULL_USER
        }
    })
}

Добавив логику в методы onCreate() и View.onConfigurationChanged() , вы сможете получить максимальные метрики окна и переопределить настройку ориентации всякий раз, когда активность изменяется в размере или перемещается между дисплеями, например, после поворота устройства или при складывании или раскладывании складного устройства. Для получения дополнительной информации о том, когда происходят изменения конфигурации и когда они приводят к повторному созданию активности, см. раздел « Обработка изменений конфигурации» .

Если вы используете Jetpack Compose, вы можете использовать ту же функцию compactScreen() в корневом компоненте Composable вашего приложения для достижения того же результата.

Ключевые моменты

  • screenOrientation : Параметр в манифесте приложения, позволяющий указать, как ваше приложение реагирует на изменение ориентации устройства.
  • Jetpack WindowManager : набор библиотек, позволяющих определять размер и соотношение сторон окна приложения; обратно совместим с API уровня 14.
  • Activity#setRequestedOrientation() : Метод, с помощью которого можно изменить ориентацию приложения во время выполнения.

Коллекции, содержащие это руководство

Данное руководство является частью подборки кратких руководств, охватывающих более широкие цели разработки под Android:

Настройте ваше приложение для обеспечения оптимизированного пользовательского интерфейса на планшетах, складных устройствах и устройствах ChromeOS.

Есть вопросы или отзывы?

Перейдите на страницу часто задаваемых вопросов и ознакомьтесь с краткими руководствами или свяжитесь с нами и поделитесь своими мыслями.