О WindowInsetsRulers

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

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

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

Недостатки WindowInsetsRulers

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

Создание пользовательских 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 отображается дополнительный отступ, поскольку отступ панели навигации не был использован родительским элементом. Дочерний элемент помещён в правильное положение на изображении справа с помощью пользовательской линейки, как показано в предыдущем примере кода.

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

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

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