WindowInsets – это стандартный API в Jetpack Compose для обработки областей экрана, которые частично или полностью скрыты системным интерфейсом. К таким областям относятся строка состояния, панель навигации и экранная клавиатура. Вы также можете передать предопределенные WindowInsetsRulers, например SafeDrawing, в Modifier.fitInside или Modifier.fitOutside, чтобы выровнять контент по системным строкам и вырезу на экране, или создать собственные WindowInsetsRulers.
Преимущества WindowInsetsRulers
- Не усложняет потребление ресурсов. Работает на этапе размещения макета. Это означает, что он полностью обходит цепочку потребления отступов и всегда может предоставить правильные абсолютные позиции системных строк и вырезов на экране, независимо от того, что сделали родительские макеты. Методы
Modifier.fitInsideиModifier.fitOutsideпомогают устранять проблемы, когда родительские composable-функции неправильно используют отступы. - Простое избегание системных строк. Помогает контенту приложения избегать системных строк и выреза на экране. Это может быть проще, чем использовать
WindowInsetsнапрямую. - Широкие возможности настройки. Разработчики могут выравнивать контент по настраиваемым линейкам и точно управлять макетами.
Недостатки WindowInsetsRulers
- Нельзя использовать для отслеживания. Поскольку этот сигнал работает на этапе размещения, информация о позиции недоступна на более раннем этапе отслеживания.
Выравнивание контента с помощью методов Modifier
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 необходимо ограничить composable-функции. Это значит, что вам нужно определить модификаторы, такие как 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.fitInside, передайте RectRuler, который принимает внутреннее значение StatusBars и CaptionBar.
@Composable fun FitInsideWithStatusAndCaptionBarDemo(modifier: Modifier) { Box( modifier = modifier .fillMaxSize() .fitInside( RectRulers.innermostOf( WindowInsetsRulers.StatusBars.current, WindowInsetsRulers.CaptionBar.current ) ) ) }
Создать собственный WindowInsetsRulers
Вы можете выравнивать контент по настраиваемым линейкам. Например, рассмотрим вариант использования, когда родительская composable-функция неправильно обрабатывает отступы, что приводит к проблемам с полями в дочернем компоненте. Эту проблему можно решить разными способами, в том числе с помощью Modifier.fitInside. Также можно создать настраиваемую линейку, чтобы точно выровнять дочернюю composable-функцию, не исправляя проблему в родительской, как показано в примере и видео ниже.
@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, вызванного родительским элементом выше по течению на изображении слева, и использование пользовательских линеек для решения проблемы справа. Под composable-функцией TextField показан дополнительный отступ, так как отступ панели навигации не был использован родительским элементом. Ребенок размещен в правильном месте на изображении справа с помощью специальной линейки, как показано в примере кода выше.
Проверьте, ограничены ли родительские элементы
Чтобы безопасно использовать WindowInsetsRulers, убедитесь, что родительский элемент предоставляет допустимые ограничения. Родительские элементы должны иметь определенный размер и не могут зависеть от размера дочернего элемента, использующего WindowInsetsRulers. Используйте fillMaxSize или другие модификаторы размера в родительских composable-функциях.
Аналогично, размещение composable-функции, использующей WindowInsetsRulers, внутри контейнера прокрутки, такого как verticalScroll, может привести к непредвиденному поведению, поскольку контейнер прокрутки предоставляет неограниченные ограничения по высоте, которые несовместимы с логикой линейки.







