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

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

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

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