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