A plataforma Android é responsável por mostrar a interface do sistema, como as
barras de status e de navegação. Essa interface do sistema é mostrada, independente 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.
Por padrão, a interface do seu app é restrita ao layout dentro da interface do sistema, como a barra de status e de navegação. Isso garante que o conteúdo do app não seja sobreposto por elementos da interface do sistema.
No entanto, recomendamos que os apps ativem a exibição nas áreas em que a interface do sistema também é mostrada, o que 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. Isso também permite que os apps sejam animados junto com a interface do sistema, especialmente ao mostrar e ocultar o teclado de software.
A ativação da exibição nessas regiões e de conteúdo por trás da interface do sistema é chamada de de ponta a ponta. Nesta página, você vai aprender sobre os diferentes tipos de encarte, como ativar a exibição de ponta a ponta e como usar as APIs de encarte para animar a interface e evitar partes ocultas do seu app.
Princípios básicos do encarte
Quando um app vai de ponta a ponta, é necessário garantir que conteúdo e interações importantes não sejam ocultos pela interface do sistema. Por exemplo, se um botão for colocado atrás da barra de navegação, o usuário pode não conseguir 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 de encarte correspondente que descreve o tamanho e onde é 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 encarte consiste em quatro dimensões de pixels: 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 a esse tipo de interface do sistema, a interface do app precisa ter esse valor inserido.
Esses tipos de encarte integrados do Android estão disponíveis pelo WindowInsets
:
Encartes que descrevem as barras de status. Essas são as barras de interface superiores do sistema que contêm ícones de notificação e outros indicadores. |
|
Os encartes da barra de status para quando ficam visíveis. Se as barras de status estiverem ocultas (devido ao login no modo de tela cheia imersivo), os encartes da barra de status principal vão estar vazios, mas esses encartes não vão estar vazios. |
|
Encartes que descrevem as barras de navegação. Essas são as barras de IU 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 durante a execução com base no método de navegação preferido do usuário e na interação com a barra de tarefas. |
|
Os encartes da barra de navegação para quando ficam visíveis. Se as barras de navegação estiverem ocultas no momento (devido ao entrar no modo de tela cheia imersivo), os encartes da barra de navegação principal vão estar vazios, mas esses encartes 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 de sistema estiverem escondidas no momento (devido ao entrar no modo de tela cheia imersivo), os encartes da barra de sistema principal vão estar vazios, mas esses encartes não vão estar. |
|
Os encartes que descrevem a quantidade de espaço na parte inferior que o teclado de software ocupa. |
|
Os encartes que descrevem a quantidade de espaço que o teclado de software ocupava antes da animação do teclado 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 encartes de elemento tocável para quando ficam visíveis. Se os elementos tocáveis estiverem ocultos (devido ao entrar no modo de tela cheia imersivo), os encartes principais do elemento tocável vão estar vazios, mas esses encartes não vão estar. |
|
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 e que não podem ser desativados pelo |
|
Os encartes representam o espaçamento necessário para evitar a sobreposição com 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 enrolar nas laterais do dispositivo. |
Esses tipos são resumidos por três tipos de encarte "seguros" que garantem que o conteúdo não seja ocultado:
Esses tipos de encarte "seguros" protegem o conteúdo de diferentes maneiras, com base nos encartes da plataforma subjacentes:
- Use
WindowInsets.safeDrawing
para proteger conteúdo que não pode ser desenhado debaixo 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 do app, como os das 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 e de gestos.
Configuração de encartes
Para permitir que o app tenha controle total sobre onde ele desenha conteúdo, siga estas etapas de configuração. Sem essas etapas, o app poderá desenhar cores pretas ou sólidas por trás da interface do sistema ou não poderá ser animado de forma síncrona com o teclado de software.
- Chame
enableEdgeToEdge()
emActivity.onCreate
. Essa chamada solicita que o app seja mostrado atrás da interface do sistema. Seu app vai controlar como esses encartes são usados para ajustar a interface. 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 do Compose
Depois que a atividade assumir o controle do processamento de todos os encartes, você poderá usar APIs do Compose para garantir que o conteúdo não seja obscurecido e que elementos interativos não se sobreponham à interface do sistema. Essas APIs também sincronizam o layout do app com mudanças de encarte.
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 da janela safeDrawing
como padding ao redor
de 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 app será desenhado atrás
da interface do sistema para atingir um efeito de ponta a ponta. Para usar a janela inteira, você precisa ajustar onde os encartes são aplicados tela por tela
ou componente por componente.
Todos esses tipos de encarte são animados automaticamente com animações do IME com backport 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 encartes de desenho seguros como padding nos quatro lados.
Há também vários métodos utilitários integrados para os tipos de encarte mais comuns.
Modifier.safeDrawingPadding()
é um desses métodos, equivalente a
Modifier.windowInsetsPadding(WindowInsets.safeDrawing)
. Há modificadores
álogos para os outros tipos de encarte.
Modificadores de tamanho de encarte
Os modificadores abaixo aplicam uma quantidade de encartes de janela definindo o tamanho do componente como o tamanho dos encartes:
Aplica o lado inicial de windowInsets como a largura (como |
|
Aplica o lado final de windowInsets como a largura (como |
|
Aplica o lado superior de windowInsets como a altura (como |
|
|
Aplica o lado 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 inserido
Os modificadores de padding do encarte (windowInsetsPadding
e auxiliares como
safeDrawingPadding
) consomem automaticamente a parte dos encartes
aplicados como padding. Ao se aprofundar na árvore de composição, os modificadores de padding
de encarte aninhados e de tamanho de encarte sabem que parte dos
encartes já foram consumidas por modificadores de padding externos e evite
usar a mesma parte dos encartes mais de uma vez, o que resultaria em muito
espaço extra.
Os modificadores de tamanho de encarte também evitarão usar a mesma parte de encartes mais de uma vez se eles já tiverem sido consumidos. No entanto, como eles estão mudando diretamente o tamanho, eles não consomem encartes.
Como resultado, os modificadores de padding aninhados mudam automaticamente a quantidade de padding aplicado a cada elemento combinável.
Analisando o mesmo exemplo de LazyColumn
de antes, o LazyColumn
está sendo
redimensionado pelo modificador imePadding
. Dentro de LazyColumn
, o último item é
dimensionado para ser a altura da parte de baixo das barras de 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 encarte está sendo consumido, e a altura do Spacer
será o tamanho
da parte de baixo das barras do sistema.
Quando o IME é aberto, os encartes do IME são animados para corresponder ao tamanho do IME, e o
modificador imePadding()
começa a aplicar o padding inferior para redimensionar o
LazyColumn
à medida que o IME é aberto. À medida que o modificador imePadding()
começa a aplicar
o padding de baixo, ele também começa a consumir essa quantidade de encartes. Portanto, a
altura da Spacer
começa a diminuir, como parte do espaçamento das barras
do sistema já foi aplicado pelo modificador imePadding()
. Depois que o
modificador imePadding()
estiver aplicando uma quantidade de padding de baixo maior
que as barras de sistema, a altura da Spacer
será 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 é realizado 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 encarte para indicar aos irmãos que uma certa quantidade de encartes
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 usa um
PaddingValues
arbitrário para consumo. Isso é útil para informar
os 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:
@OptIn(ExperimentalLayoutApi::class) Column(Modifier.padding(16.dp).consumeWindowInsets(PaddingValues(16.dp))) { // content Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.ime)) }
Nos casos em que os encartes brutos de janela são necessários sem consumo, use os
valores WindowInsets
diretamente ou use WindowInsets.asPaddingValues()
para
retornar um PaddingValues
dos encartes que não são afetados pelo consumo.
No entanto, devido às ressalvas abaixo, prefira usar os modificadores de padding
de encarte de janela e de tamanho de encarte de janela sempre que possível.
Encartes 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 encartes é 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 foram criados para atrasar o uso dos valores dos encartes até a fase de layout, o que garante que os valores do encarte sejam usados no mesmo frame em que são atualizados.
Animações do IME do teclado com WindowInsets
Você pode 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") } } }
Figura 1. Animações do IME (editor de método de entrada, na sigla em inglês).
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 encartes com base em como os elementos são colocados no app
de acordo com as especificações do Material Design.
Inserção no processamento de elementos combináveis
Confira abaixo uma lista dos componentes do Material Design que processam automaticamente os encartes.
Barras de apps
TopAppBar
/SmallTopAppBar
/CenterAlignedTopAppBar
/MediumTopAppBar
/LargeTopAppBar
: aplica os lados superiores e horizontais das barras do sistema como padding, já que são usados na parte de cima da janela.BottomAppBar
: aplica as laterais inferior e horizontais 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 encartes vertical e início ao conteúdo.ModalBottomSheet
: aplica os encartes inferiores.NavigationBar
: aplica os encartes inferior e horizontais.NavigationRail
: aplica os encartes de vertical e início.
Scaffold
Por padrão,
Scaffold
fornece encartes como parâmetro paddingValues
para você consumir e usar.
Scaffold
não aplica os encartes 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
É possível mudar o parâmetro windowInsets
transmitido ao elemento combinável para
configurar o comportamento dele. Esse parâmetro pode ser um tipo diferente de
encarte de janela a ser aplicado 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 encartes padrão quando a tela tiver o código das visualizações e do Compose na mesma hierarquia. Nesse caso, é necessário deixar claro qual deles vai consumir os encartes e qual deles deve ignorá-los.
Por exemplo, se o layout mais externo for um layout de visualização do Android, consuma os encartes no sistema de visualização e ignore-os no 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
.
Recursos
- Now in Android (link em inglês): um app Android totalmente funcional criado inteiramente com Kotlin e Jetpack Compose.
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