A plataforma Android é responsável por desenhar a interface do sistema, como a barra de status e a barra de navegação. Essa interface do sistema é exibida independentemente do app que o usuário está usando.
WindowInsets
fornece informações sobre a interface
do sistema para garantir que o app desenhe na área correta e que a interface não seja obscurecida
pela interface do sistema.
No Android 14 (nível 34 da API) e versões anteriores, a interface do app não é renderizada abaixo das barras do sistema e dos cortes de tela por padrão.
No Android 15 (nível 35 da API) e versões mais recentes, o app é renderizado abaixo das barras do sistema e exibe recortes quando o app é direcionado ao SDK 35. Isso resulta em uma experiência do usuário mais integrada e permite que o app aproveite ao máximo o espaço da janela disponível.
Mostrar conteúdo atrás da interface do sistema é chamado de exibição de ponta a ponta. Nesta página, você vai aprender sobre os diferentes tipos de insets, como ir de borda a borda e como usar as APIs inset para animar a interface e garantir que o conteúdo do app não seja obscurecido por elementos da interface do sistema.
Conceitos básicos de inserção
Quando um app é exibido de ponta a ponta, é necessário garantir que o conteúdo e as interações importantes não sejam obscurecidos pela interface do sistema. Por exemplo, se um botão for colocado atrás da barra de navegação, o usuário não poderá clicar nele.
O tamanho da interface do sistema e as informações sobre onde ela é colocada são especificados por encartes.
Cada parte da interface do sistema tem um tipo correspondente de inseto que descreve o tamanho e o local onde ele é colocado. Por exemplo, os encartes da barra de status fornecem o tamanho e a posição da barra de status, enquanto os encartes da barra de navegação fornecem o tamanho e a posição da barra de navegação. Cada tipo de inseto consiste em quatro dimensões de pixel: superior, esquerda, direita e inferior. Essas dimensões especificam o quanto a interface do sistema se estende dos lados correspondentes da janela do app. Para evitar a sobreposição com esse tipo de interface do sistema, a interface do app precisa ser inserida nesse valor.
Esses tipos de encarte integrados do Android estão disponíveis pelo WindowInsets
:
Encartes que descrevem as barras de status. São as barras da interface do sistema na parte de cima que contêm ícones de notificação e outros indicadores. |
|
Os engastes da barra de status quando ela está visível. Se as barras de status estiverem ocultas (por causa da entrada no modo de tela cheia imersiva), os insetos da barra de status principal vão estar vazios, mas não vão estar vazios. |
|
Os insetos descrevendo as barras de navegação. São as barras da interface do sistema no lado esquerdo, direito ou inferior do dispositivo, que descrevem a barra de tarefas ou os ícones de navegação. Eles podem mudar no momento da execução com base no método de navegação preferido do usuário e na interação com a barra de tarefas. |
|
Os engastes da barra de navegação quando eles estão visíveis. Se as barras de navegação estiverem ocultas (devido à entrada no modo de tela cheia imersiva), os insetos da barra de navegação principal vão estar vazios, mas não vão estar vazios. |
|
O encarte descrevendo a decoração da janela da interface do sistema se estiver em uma janela de formato livre, como a barra de título superior. |
|
Os encartes da barra de legenda para quando estão visíveis. Se as barras de legenda estiverem ocultas no momento, os encartes da barra de legenda principal estarão vazios, mas não estarão. |
|
A união dos encartes da barra do sistema, que incluem as barras de status, de navegação e de legenda. |
|
Os encartes da barra do sistema para quando ficam visíveis. Se as barras do sistema estiverem ocultas (por entrar no modo de tela cheia imersiva), os enfeites da barra principal do sistema vão estar vazios, mas não vão estar vazios. |
|
Os insetos descrevem a quantidade de espaço na parte de baixo que o teclado de software ocupa. |
|
Os insetos descrevem a quantidade de espaço que o teclado de software ocupava antes da animação atual. |
|
Os encartes que descrevem a quantidade de espaço que o teclado de software vai ocupar após a animação do teclado atual. |
|
Um tipo de encarte que descreve informações mais detalhadas sobre a interface de navegação, informando a quantidade de espaço em que os "toques" serão processados pelo sistema, e não pelo app. Para barras de navegação transparentes com navegação por gestos, é possível tocar em alguns elementos do app na interface de navegação do sistema. |
|
Os elementos inseridos que podem ser tocados quando estão visíveis. Se os elementos que podem ser tocados estiverem ocultos (por causa da entrada no modo de tela cheia imersiva), os insetos principais do elemento que pode ser tocado vão estar vazios, mas não vão estar vazios. |
|
Os encartes que representam a quantidade de encartes em que o sistema vai interceptar gestos para navegação. Os apps podem especificar manualmente o processamento de uma quantidade limitada desses gestos usando o |
|
Um subconjunto dos gestos do sistema que sempre serão processados pelo sistema e não podem ser desativados por |
|
Os encartes representam o espaçamento necessário para evitar a sobreposição a um corte da tela (entalhe ou pinhole). |
|
Encartes que representam as áreas curvas de uma exibição em cascata. Uma tela em cascata tem áreas curvas ao longo das bordas, onde a tela começa a se curvar ao longo das laterais do dispositivo. |
Esses tipos são resumidos por três tipos de inserção "seguros" que garantem que o conteúdo não seja obstruído:
Esses tipos de insetos "seguros" protegem o conteúdo de maneiras diferentes, com base nos insetos da plataforma:
- Use
WindowInsets.safeDrawing
para proteger conteúdo que não pode ser exibido abaixo de nenhuma interface do sistema. Este é o uso mais comum de encartes: para evitar mostrar conteúdo ocultado pela interface do sistema (parcial ou completamente). - Use
WindowInsets.safeGestures
para proteger o conteúdo com gestos. Isso evita que os gestos do sistema entrem em conflito com os gestos do app (como os de páginas inferiores, carrosséis ou jogos). - Use
WindowInsets.safeContent
como uma combinação deWindowInsets.safeDrawing
eWindowInsets.safeGestures
para garantir que o conteúdo não tenha sobreposição visual nem de gestos.
Configuração de encartes
Para permitir que o app tenha controle total sobre onde ele extrai conteúdo, siga estas etapas de configuração. Sem essas etapas, o app pode desenhar cores pretas ou sólidas atrás da interface do sistema ou não animar de forma síncrona com o teclado do software.
- Segmente o SDK 35 ou mais recente para forçar a tela cheia no Android 15 e versões mais recentes. O app é exibido atrás da interface do sistema. É possível ajustar a interface do app processando encartes.
- Opcionalmente, chame
enableEdgeToEdge()
emActivity.onCreate()
, o que permite que o app seja exibido de ponta a ponta em versões anteriores do Android. Defina
android:windowSoftInputMode="adjustResize"
na entradaAndroidManifest.xml
da atividade. Essa configuração permite que o app receba o tamanho do IME de software como encartes, que podem ser usados para preencher e posicionar o conteúdo adequadamente quando o IME aparecer e desaparecer no app.<!-- in your AndroidManifest.xml file: --> <activity android:name=".ui.MainActivity" android:label="@string/app_name" android:windowSoftInputMode="adjustResize" android:theme="@style/Theme.MyApplication" android:exported="true">
APIs Compose
Depois que a atividade assumir o controle de todos os insets, você poderá usar as APIs do Compose para garantir que o conteúdo não seja obscurecido e que os elementos interativos não se sobreponham à interface do sistema. Essas APIs também sincronizam o layout do app com as mudanças de inset.
Por exemplo, esse é o método mais básico de aplicar os encartes ao conteúdo do app inteiro:
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() setContent { Box(Modifier.safeDrawingPadding()) { // the rest of the app } } }
Esse snippet aplica os encartes de janela safeDrawing
como padding em todo o
conteúdo do app. Embora isso garanta que os elementos interativos não
se sobreponham à interface do sistema, também significa que nenhum elemento do app será exibido atrás
da interface do sistema para alcançar um efeito de borda a borda. Para aproveitar toda a
janela, você precisa ajustar onde os insets são aplicados em cada tela
ou componente.
Todos esses tipos de inseto são animados automaticamente com animações de IME reportadas para a API 21. Por extensão, todos os layouts que usam esses encartes também são animados automaticamente à medida que os valores do encarte mudam.
Há duas maneiras principais de usar esses tipos de encarte para ajustar os layouts combináveis: modificadores de padding e modificadores de tamanho de encarte.
Modificadores de padding
Modifier.windowInsetsPadding(windowInsets: WindowInsets)
aplica os
encartes de janela fornecidos como padding, agindo da mesma forma que o Modifier.padding
faria.
Por exemplo, Modifier.windowInsetsPadding(WindowInsets.safeDrawing)
aplica
os engastes de desenho seguros como padding nos quatro lados.
Há também vários métodos utilitários integrados para os tipos de inset mais comuns.
Modifier.safeDrawingPadding()
é um desses métodos, equivalente a
Modifier.windowInsetsPadding(WindowInsets.safeDrawing)
. Há modificadores
análogos para os outros tipos de inset.
Modificadores de tamanho de inserção
Os modificadores a seguir aplicam uma quantidade de engastes de janela definindo o tamanho do componente como o tamanho dos engastes:
Aplica o lado inicial de windowInsets como a largura (como |
|
Aplica o lado final de windowInsets como a largura (como |
|
Aplica a parte de cima de windowInsets como a altura (como |
|
|
Aplica a parte de baixo de windowInsets como a altura (como |
Esses modificadores são especialmente úteis para dimensionar um Spacer
que ocupa o
espaço de encartes:
LazyColumn( Modifier.imePadding() ) { // Other content item { Spacer( Modifier.windowInsetsBottomHeight( WindowInsets.systemBars ) ) } }
Consumo de Inset
Os modificadores de padding de inserção (windowInsetsPadding
e auxiliares como
safeDrawingPadding
) consomem automaticamente a parte dos insets que são
aplicadas como padding. Ao se aprofundar na árvore de composição, os modificadores de padding de
encartes aninhados e os modificadores de tamanho de encartes sabem que parte dos
encartes já foi consumida por modificadores de padding de encartes externos e evitam
usar a mesma parte dos encartes mais de uma vez, o que resultaria em muito
espaço extra.
Os modificadores de tamanho de encartes também evitam usar a mesma parte de encartes mais de uma vez se os encartes já foram consumidos. No entanto, como eles estão mudando diretamente o tamanho, eles não consomem encartes.
Como resultado, o aninhamento de modificadores de padding muda automaticamente a quantidade de padding aplicado a cada elemento combinável.
Analisando o mesmo exemplo de LazyColumn
anterior, o LazyColumn
está sendo
redimensionado pelo modificador imePadding
. Dentro do LazyColumn
, o último item é
dimensionado para ser a altura da parte de baixo das barras do sistema:
LazyColumn( Modifier.imePadding() ) { // Other content item { Spacer( Modifier.windowInsetsBottomHeight( WindowInsets.systemBars ) ) } }
Quando o IME é fechado, o modificador imePadding()
não aplica padding, já que
o IME não tem altura. Como o modificador imePadding()
não está aplicando padding,
nenhum inseto está sendo consumido, e a altura da Spacer
será o tamanho da
parte de baixo das barras do sistema.
Quando o IME é aberto, os insets são animados para corresponder ao tamanho do IME, e o
modificador imePadding()
começa a aplicar o padding inferior para redimensionar o
LazyColumn
conforme o IME é aberto. À medida que o modificador imePadding()
começa a aplicar
o padding inferior, ele também começa a consumir essa quantidade de insets. Portanto, a
altura da Spacer
começa a diminuir, como parte do espaçamento das barras
do sistema já foi aplicado pelo modificador imePadding()
. Quando o
modificador imePadding()
aplica um valor de padding inferior maior
que as barras do sistema, a altura do Spacer
é zero.
Quando o IME é fechado, as mudanças acontecem no sentido inverso: o Spacer
começa a
se expandir de uma altura de zero quando imePadding()
é aplicado menos do que o
lado de baixo das barras do sistema, até que finalmente o Spacer
corresponda à altura
da parte de baixo das barras do sistema quando o IME estiver completamente animado.
Esse comportamento é alcançado pela comunicação entre todos os
modificadores windowInsetsPadding
e pode ser influenciado de algumas outras
maneiras.
Modifier.consumeWindowInsets(insets: WindowInsets)
também consome encartes
da mesma forma que Modifier.windowInsetsPadding
, mas não aplica
os encartes consumidos como padding. Isso é útil em combinação com os modificadores de tamanho
de insetos, para indicar a elementos irmãos que uma determinada quantidade de insetos
já foi consumida:
Column(Modifier.verticalScroll(rememberScrollState())) { Spacer(Modifier.windowInsetsTopHeight(WindowInsets.systemBars)) Column( Modifier.consumeWindowInsets( WindowInsets.systemBars.only(WindowInsetsSides.Vertical) ) ) { // content Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.ime)) } Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.systemBars)) }
O Modifier.consumeWindowInsets(paddingValues: PaddingValues)
se comporta de maneira muito
semelhante à versão com um argumento WindowInsets
, mas consome um
PaddingValues
arbitrário. Isso é útil para informar
aos filhos quando o padding ou o espaçamento é fornecido por algum outro mecanismo além dos
modificadores de padding do encarte, como um Modifier.padding
comum ou espaçadores de
altura fixa:
Column(Modifier.padding(16.dp).consumeWindowInsets(PaddingValues(16.dp))) { // content Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.ime)) }
Nos casos em que os insets de janela brutos são necessários sem consumo, use os
valores de WindowInsets
diretamente ou use WindowInsets.asPaddingValues()
para
retornar um PaddingValues
dos insetos que não são afetados pelo consumo.
No entanto, devido às ressalvas abaixo, prefira usar os modificadores de preenchimento
e de tamanho de encartes de janela sempre que possível.
Insets e fases do Jetpack Compose
O Compose usa as APIs principais do AndroidX para atualizar e animar encartes, que usam as APIs da plataforma para gerenciar encartes. Devido a esse comportamento da plataforma, os encartes têm uma relação especial com as fases do Jetpack Compose.
O valor dos insets é atualizado após a fase de composição, mas antes da fase de layout. Isso significa que a leitura do valor dos encartes na composição geralmente usa um valor dos encartes que está um frame atrasado. Os modificadores integrados descritos nesta página são criados para atrasar o uso dos valores das informações até a fase de layout, o que garante que os valores sejam usados no mesmo frame em que são atualizados.
Animações de IME do teclado com WindowInsets
É possível aplicar Modifier.imeNestedScroll()
a um contêiner de rolagem para abrir e
fechar o IME automaticamente ao rolar até a parte de baixo do contêiner.
class WindowInsetsExampleActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) WindowCompat.setDecorFitsSystemWindows(window, false) setContent { MaterialTheme { MyScreen() } } } } @OptIn(ExperimentalLayoutApi::class) @Composable fun MyScreen() { Box { LazyColumn( modifier = Modifier .fillMaxSize() // fill the entire window .imePadding() // padding for the bottom for the IME .imeNestedScroll(), // scroll IME at the bottom content = { } ) FloatingActionButton( modifier = Modifier .align(Alignment.BottomEnd) .padding(16.dp) // normal 16dp of padding for FABs .navigationBarsPadding() // padding for navigation bar .imePadding(), // padding for when IME appears onClick = { } ) { Icon(imageVector = Icons.Filled.Add, contentDescription = "Add") } } }
Suporte a encarte para os componentes do Material 3
Para facilitar o uso, muitos dos elementos combináveis integrados do Material 3
(androidx.compose.material3
)
processam os insetos por conta própria, com base em como os elementos combináveis são colocados no app
de acordo com as especificações do Material.
Inserção no processamento de elementos combináveis
Confira abaixo uma lista dos componentes do Material Design que processam automaticamente os insets.
Barras de apps
TopAppBar
/SmallTopAppBar
/CenterAlignedTopAppBar
/MediumTopAppBar
/LargeTopAppBar
: aplica os lados superior e horizontal das barras do sistema como padding, já que é usado na parte de cima da janela.BottomAppBar
: aplica os lados inferior e horizontal das barras do sistema como padding.
Contêineres de conteúdo
ModalDrawerSheet
/DismissibleDrawerSheet
/PermanentDrawerSheet
(conteúdo dentro de uma gaveta de navegação modal): aplica engastes verticais e iniciais ao conteúdo.ModalBottomSheet
: aplica os insetos inferiores.NavigationBar
: aplica os engastes inferior e horizontal.NavigationRail
: aplica os engastes vertical e start.
Scaffold
Por padrão,
Scaffold
fornece insets como parâmetro paddingValues
para você consumir e usar.
O Scaffold
não aplica os insetos ao conteúdo. Essa responsabilidade é sua.
Por exemplo, para consumir esses encartes com um LazyColumn
dentro de um Scaffold
:
Scaffold { innerPadding -> // innerPadding contains inset information for you to use and apply LazyColumn( // consume insets as scaffold doesn't do it by default modifier = Modifier.consumeWindowInsets(innerPadding), contentPadding = innerPadding ) { items(count = 100) { Box( Modifier .fillMaxWidth() .height(50.dp) .background(colors[it % colors.size]) ) } } }
Substituir encartes padrão
Você pode mudar o parâmetro windowInsets
transmitido ao elemento combinável para
configurar o comportamento dele. Esse parâmetro pode ser um tipo diferente de
janela inserida para aplicar ou desativado transmitindo uma instância vazia:
WindowInsets(0, 0, 0, 0)
.
Por exemplo, para desativar o processamento do encarte em
LargeTopAppBar
,
defina o parâmetro windowInsets
como uma instância vazia:
LargeTopAppBar( windowInsets = WindowInsets(0, 0, 0, 0), title = { Text("Hi") } )
Interoperabilidade com encartes do sistema de visualização
Talvez seja necessário substituir os insets padrão quando a tela tiver visualizações e código do Compose na mesma hierarquia. Nesse caso, é necessário especificar qual deles deve consumir os insetos e qual deve ignorá-los.
Por exemplo, se o layout mais externo for um layout de visualização do Android, consuma os insets no sistema de visualização e ignore-os para o Compose.
Como alternativa, caso o layout mais externo seja um elemento combinável, consuma os
encartes no Compose e preencha os elementos AndroidView
adequadamente.
Por padrão, cada ComposeView
consome todos os encartes no
nível de consumo WindowInsetsCompat
. Para mudar esse comportamento padrão, defina
ComposeView.consumeWindowInsets
como false
.
Proteção da barra de sistema
Quando o app é direcionado ao SDK 35 ou mais recente, o conteúdo é aplicado de borda a borda. A barra de status do sistema e as barras de navegação por gestos são transparentes, mas a barra de navegação com três botões é translúcida.
Para remover a proteção em segundo plano padrão da navegação com três botões,
defina Window.setNavigationBarContrastEnforced
como false
.
Recursos
- Now in Android: um app Android totalmente funcional criado com o Kotlin e o Jetpack Compose.
- Processar restrições de ponta a ponta no Android 15: um codelab que explica a restrição de ponta a ponta no Android 15.
- Dicas de processamento de insetos para a restrição de ponta a ponta do Android 15
- Visualizar e testar a interface de ponta a ponta do app
- 3 coisas para melhorar a experiência do seu app Android: de ponta a ponta, volta preditiva e resumo: um vídeo do YouTube sobre a aplicação de ponta a ponta do Android 15.
- Edge-to-edge and insets | Compose Tips: um vídeo do YouTube mostrando como lidar com insets para desenhar de borda a borda
Recomendados para você
- Observação: o texto do link aparece quando o JavaScript está desativado.
- Componentes e layouts do Material Design
- Migrar
CoordinatorLayout
para o Compose - Outras considerações