О WindowInsetsRulers

WindowInsets — это стандартный API в Jetpack Compose для управления областями экрана, которые частично или полностью скрыты системным интерфейсом. К таким областям относятся строка состояния, панель навигации и экранная клавиатура. В качестве альтернативы вы можете передавать предопределенные WindowInsetsRulers такие как SafeDrawing , в Modifier.fitInside или Modifier.fitOutside , чтобы выровнять содержимое по системным панелям и вырезу на экране, или создавать собственные WindowInsetsRulers .

Преимущества линеек WindowInsetsRulers

  • Избегает сложностей, связанных с потреблением ресурсов : работает на этапе размещения элементов компоновки. Это означает, что полностью обходит цепочку потребления отступов и всегда может обеспечить правильное, абсолютное позиционирование системных панелей и вырезов для отображения, независимо от того, что сделали родительские элементы компоновки. Использование методов Modifier.fitInside или Modifier.fitOutside помогает исправить проблемы, когда родительские элементы компоновки некорректно потребляют отступы.
  • Легко избегайте системных панелей : это помогает содержимому вашего приложения избегать системных панелей и выреза на экране, и может быть проще, чем использовать WindowInsets напрямую.
  • Высокая степень настраиваемости : разработчики могут выравнивать контент по пользовательским линейкам и точно контролировать макеты с помощью настраиваемых макетов.

Недостатки WindowInsetsRulers

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

Выравнивайте контент с помощью методов-модификаторов.

Modifier.fitInside позволяет приложениям выравнивать контент по системным панелям и отображать вырезы. Его можно использовать вместо WindowInsets . Modifier.fitOutside обычно является обратной функцией к Modifier.fitInside .

Например, чтобы убедиться, что содержимое приложения не заходит на системные полосы и вырез в экране, можно использовать fitInside(WindowInsetsRulers.safeDrawing.current) .

@Composable
fun FitInsideDemo(modifier: Modifier) {
    Box(
        modifier = modifier
            .fillMaxSize()
            // Or DisplayCutout, Ime, NavigationBars, StatusBar, etc...
            .fitInside(WindowInsetsRulers.SafeDrawing.current)
    )
}

В следующей таблице показано, как будет выглядеть содержимое вашего приложения с предопределенными линейками, имеющими параметры Modifier.fitInside или Modifier.fitOutside .

Предопределенный тип линейки

Modifier.fitInside

Modifier.fitOutside

DisplayCutout

Ime

Н/Д

NavigationBars

SafeDrawing

Н/Д (вместо этого используйте StatusBar , CaptionBar , NavigationBar )

StatusBar

Использование Modifier.fitInside и Modifier.fitOutside требует, чтобы компонуемые объекты были ограничены. Это означает, что необходимо определить модификаторы, такие как Modifier.size или Modifier.fillMaxSize .

Некоторые линейки, такие как Modifier.fitOutside в SafeDrawing и SystemBars возвращают несколько линеек. В этом случае Android размещает Composable, используя одну линейку слева, сверху, справа и снизу.

Избегайте использования IME с помощью Modifier.fitInside

Для обработки нижних элементов с помощью IME с Modifier.fitInside передайте объект RectRuler , который принимает значение самого внутреннего элемента NavigationBar и Ime .

@Composable
fun FitInsideWithImeDemo(modifier: Modifier) {
    Box(
        modifier = modifier
            .fillMaxSize()
            .fitInside(
                RectRulers.innermostOf(
                    WindowInsetsRulers.NavigationBars.current,
                    WindowInsetsRulers.Ime.current
                )
            )
    ) {
        TextField(
            value = "Demo IME Insets",
            onValueChange = {},
            modifier = modifier.align(Alignment.BottomStart).fillMaxWidth()
        )
    }
}

Избегайте строки состояния и строки заголовка с помощью Modifier.fitInside

Аналогично, чтобы убедиться, что верхние элементы избегают строки состояния и строки заголовка, а также Modifier.fitInsider , передайте RectRuler , который принимает самое внутреннее значение из StatusBars и CaptionBar .

@Composable
fun FitInsideWithStatusAndCaptionBarDemo(modifier: Modifier) {
    Box(
        modifier = modifier
            .fillMaxSize()
            .fitInside(
                RectRulers.innermostOf(
                    WindowInsetsRulers.StatusBars.current,
                    WindowInsetsRulers.CaptionBar.current
                )
            )
    )
}

Создайте пользовательские WindowInsetsRulers

Вы можете выравнивать контент по пользовательским линейкам. Например, рассмотрим случай, когда родительский компонент некорректно обрабатывает отступы, вызывая проблемы с отступами в дочернем компоненте. Хотя эту проблему можно решить и другими способами, в том числе с помощью Modifier.fitInside , вы также можете создать пользовательскую линейку для точного выравнивания дочернего компонента без необходимости исправлять проблему в родительском компоненте, как показано в следующем примере и видео:

@Composable
fun WindowInsetsRulersDemo(modifier: Modifier) {
    Box(
        contentAlignment = BottomCenter,
        modifier = modifier
            .fillMaxSize()
            // The mistake that causes issues downstream, as .padding doesn't consume insets.
            // While it's correct to instead use .windowInsetsPadding(WindowInsets.navigationBars),
            // assume it's difficult to identify this issue to see how WindowInsetsRulers can help.
            .padding(WindowInsets.navigationBars.asPaddingValues())
    ) {
        TextField(
            value = "Demo IME Insets",
            onValueChange = {},
            modifier = modifier
                // Use alignToSafeDrawing() instead of .imePadding() to precisely place this child
                // Composable without having to fix the parent upstream.
                .alignToSafeDrawing()

            // .imePadding()
            // .fillMaxWidth()
        )
    }
}

fun Modifier.alignToSafeDrawing(): Modifier {
    return layout { measurable, constraints ->
        if (constraints.hasBoundedWidth && constraints.hasBoundedHeight) {
            val placeable = measurable.measure(constraints)
            val width = placeable.width
            val height = placeable.height
            layout(width, height) {
                val bottom = WindowInsetsRulers.SafeDrawing.current.bottom
                    .current(0f).roundToInt() - height
                val right = WindowInsetsRulers.SafeDrawing.current.right
                    .current(0f).roundToInt()
                val left = WindowInsetsRulers.SafeDrawing.current.left
                    .current(0f).roundToInt()
                measurable.measure(Constraints.fixed(right - left, height))
                    .place(left, bottom)
            }
        } else {
            val placeable = measurable.measure(constraints)
            layout(placeable.width, placeable.height) {
                placeable.place(0, 0)
            }
        }
    }
}

В следующем видео показан пример проблемного использования отступов IME, вызванного родительским элементом на изображении слева, и использование пользовательских линеек для решения проблемы справа. Под элементом TextField Composable отображается дополнительный отступ, поскольку отступы панели навигации не были учтены родительским элементом. Дочерний элемент размещен в правильном месте на изображении справа с помощью пользовательской линейки, как показано в предыдущем примере кода.

Убедитесь, что родители ограничены.

Для безопасного использования WindowInsetsRulers убедитесь, что родительский элемент предоставляет допустимые ограничения. Родительские элементы должны иметь заданный размер и не могут зависеть от размера дочернего элемента, использующего WindowInsetsRulers . Используйте fillMaxSize или другие модификаторы размера для родительских элементов Composable.

Аналогично, размещение компонуемого объекта, использующего WindowInsetsRulers внутри прокручиваемого контейнера, такого как verticalScroll может привести к неожиданному поведению, поскольку прокручиваемый контейнер предоставляет неограниченные ограничения по высоте, которые несовместимы с логикой линейки.