O Jetpack Compose facilita muito o processo de projetar e criar a IU do app. O Compose transforma o estado em elementos da IU usando:
- Composição de elementos
- Layout dos elementos
- Desenho de elementos
Este documento se concentra no layout dos elementos, explicando alguns dos recursos básicos oferecidos pelo Compose para ajudar no layout dos elementos da IU.
Metas de layout no Compose
A implementação do sistema de layout do Jetpack Compose tem duas metas principais:
- Alto desempenho
- Capacidade de criar layouts personalizados facilmente
Noções básicas das funções combináveis
Funções que podem ser compostas são o elemento básico fundamental do Compose. Uma função
que pode ser composta é uma função que emite uma Unit
de descrição de alguma parte da IU. A função recebe
alguma entrada e gera o que será exibido na tela. Para ver mais
informações sobre funções que podem ser compostas, consulte a documentação Modelo mental
do Compose.
Uma função combinável pode emitir vários elementos da interface. No entanto, se você não fornecer orientações sobre como eles devem ser organizados, o Compose poderá organizar os elementos de uma maneira que você não quer. Por exemplo, este código gera dois elementos de texto:
@Composable fun ArtistCard() { Text("Alfred Sisley") Text("3 minutes ago") }
Sem orientação sobre como você quer organizá-los, o Compose colocará os elementos de texto um sobre o outro, o que os deixará ilegíveis:
O Compose fornece uma coleção de layouts prontos para uso que ajudam você a organizar seus elementos da IU e facilita a definição dos seus layouts mais especializados.
Componentes de layout padrão
Em muitos casos, você pode simplesmente usar os elementos de layout padrão do Compose.
Use
Column
para colocar itens na tela verticalmente.
@Composable fun ArtistCardColumn() { Column { Text("Alfred Sisley") Text("3 minutes ago") } }
Da mesma forma, use
Row
para colocar itens na tela horizontalmente. Tanto Column
quanto Row
são compatíveis com
a configuração de alinhamento dos elementos que eles contêm.
@Composable fun ArtistCardRow(artist: Artist) { Row(verticalAlignment = Alignment.CenterVertically) { Image(bitmap = artist.image, contentDescription = "Artist image") Column { Text(artist.name) Text(artist.lastSeenOnline) } } }
Use Box
para colocar elementos uns sobre os outros. A Box
também oferece suporte à configuração de alinhamento específico dos elementos que ela contém.
@Composable fun ArtistAvatar(artist: Artist) { Box { Image(bitmap = artist.image, contentDescription = "Artist image") Icon(Icons.Filled.Check, contentDescription = "Check mark") } }
Geralmente, esses elementos de criação são tudo o que você precisa. Você pode criar sua própria função que pode ser composta para combinar esses layouts em um mais elaborado, que seja adequado ao seu app.
Para definir a posição dos filhos em uma Row
, defina os argumentos horizontalArrangement
e
verticalAlignment
. Para uma Column
, defina os argumentos verticalArrangement
e
horizontalAlignment
:
@Composable fun ArtistCardArrangement(artist: Artist) { Row( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.End ) { Image(bitmap = artist.image, contentDescription = "Artist image") Column { /*...*/ } } }
Modelo de layout
No modelo de layout, a árvore de IU é exibida em uma única transmissão. Primeiro, cada nó precisa medir a si mesmo, e depois medir todos os filhos recursivamente, transmitindo as restrições de tamanho para baixo aos filhos. Depois, os nós de folhas são dimensionados e posicionados, com os tamanhos resolvidos e as instruções de posicionamento retornados para a árvore.
Em resumo, os pais medem antes dos filhos, mas são dimensionados e colocados depois dos filhos.
Considere a seguinte função SearchResult
.
@Composable fun SearchResult() { Row { Image( // ... ) Column { Text( // ... ) Text( // ... ) } } }
Essa função produz a seguinte árvore de IU.
SearchResult
Row
Image
Column
Text
Text
No exemplo do SearchResult
, o layout da árvore de IU segue esta ordem:
- É solicitado que o nó raiz
Row
faça a medição. - O nó raiz
Row
pede para o primeiro filho,Image
, medir. - O
Image
é um nó de folha (ou seja, não tem filhos). Portanto, ele informa um tamanho e retorna as instruções de posicionamento. - O nó raiz
Row
pede para o segundo filho,Column
, medir. - O nó
Column
pede ao primeiro filhoText
para medir. - O primeiro nó
Text
é um nó de folha. Portanto, ele informa um tamanho e retorna as instruções de posicionamento. - O nó
Column
pede ao segundo filhoText
para medir. - O segundo nó
Text
é um nó de folha. Portanto, ele informa um tamanho e retorna as instruções de posicionamento. - Agora que o nó
Column
mediu, dimensionou e posicionou os filhos, ele pode determinar o próprio tamanho e posicionamento. - Agora que o nó raiz
Row
mediu, dimensionou e posicionou os filhos, ele pode determinar o próprio tamanho e posicionamento.
Desempenho
O Compose alcança um alto desempenho medindo os filhos apenas uma vez. A medição de passagem única é boa para o desempenho, possibilitando que o Compose processe árvores de IU profundas com eficiência. Se um elemento de layout medisse o filho duas vezes e esse filho medisse cada filho dele duas vezes, e assim por diante, uma única tentativa de dispor uma IU inteira tomaria muito trabalho, dificultando a manutenção do bom desempenho do app.
Caso seu layout precise de várias medidas por algum motivo, o Compose oferece um sistema especial para isso, as medições intrínsecas. Leia mais sobre esse recurso em Medidas intrínsecas em layouts do Compose.
Como a medição e a colocação são subfases distintas da transmissão de layout, qualquer mudança que afete apenas a colocação de itens, e não a medição, pode ser executada separadamente.
Como usar modificadores em layouts
Como discutido em Modificadores do Compose, você pode usar
modificadores para decorar ou aumentar as funções que podem ser compostas. Modificadores são essenciais
para personalizar o layout. Por exemplo, encadeamos vários modificadores
para personalizar o ArtistCard
:
@Composable fun ArtistCardModifiers( artist: Artist, onClick: () -> Unit ) { val padding = 16.dp Column( Modifier .clickable(onClick = onClick) .padding(padding) .fillMaxWidth() ) { Row(verticalAlignment = Alignment.CenterVertically) { /*...*/ } Spacer(Modifier.size(padding)) Card( elevation = CardDefaults.cardElevation(defaultElevation = 4.dp), ) { /*...*/ } } }
Observe diferentes funções de modificador sendo usadas juntas no código acima.
clickable
gera uma reação que pode ser composta à entrada do usuário e exibe uma ondulação.padding
cria um espaço ao redor de um elemento.fillMaxWidth
faz a função que pode ser composta preencher a largura máxima atribuída a ela pelo elemento pai.size()
especifica a largura e a altura preferenciais de um elemento.
Layouts roláveis
Para saber mais sobre layouts roláveis, consulte a documentação sobre gestos no Compose.
Para listas comuns e lentas, consulte a documentação sobre listas do Compose.
Layouts responsivos
Um layout precisa ser projetado pensando em diferentes orientações de tela e tamanhos. O Compose oferece alguns mecanismos prontos para facilitar a adaptação dos layouts de funções que podem ser compostas a diferentes configurações de tela.
Restrições
Para saber quais são as restrições provenientes do pai e projetar o layout
de acordo com isso, é necessário usar BoxWithConstraints
. As restrições de
medidas
podem ser encontradas no escopo do lambda do conteúdo. Essas restrições de medidas podem ser usadas
para compor diferentes layouts para diferentes configurações de tela:
@Composable fun WithConstraintsComposable() { BoxWithConstraints { Text("My minHeight is $minHeight while my maxWidth is $maxWidth") } }
Layouts baseados em slot
O Compose oferece uma grande variedade de funções baseadas no Material
Design com a dependência androidx.compose.material:material
(incluída ao criar o projeto do Compose no Android Studio) para facilitar a criação da IUs. Elementos como
Drawer
,
FloatingActionButton
e TopAppBar
são todos fornecidos pelo Compose.
Componentes do Material Design usam muito as APIs de slot, um padrão que foi introduzido no Compose
para oferecer uma camada de personalização, além dos elementos que podem ser compostos. Essa abordagem faz
com que os componentes sejam mais flexíveis, porque eles aceitam um elemento filho que pode se configurar.
Portanto, não é mais necessário expor todos os parâmetros de configuração do filho.
Os slots deixam um espaço vazio na IU, que o desenvolvedor pode preencher como quiser. Por
exemplo, estes são os slots que podem ser personalizados em um
TopAppBar
:
Os elementos que podem ser compostos geralmente usam um lambda que pode ser composto content
( content: @Composable
() -> Unit
). As APIs de slot expõem vários parâmetros content
para usos específicos.
Por exemplo, TopAppBar
permite que você forneça o conteúdo para title
,
navigationIcon
e actions
.
Por exemplo,
Scaffold
permite implementar uma IU com a estrutura básica de layout do Material Design.
Scaffold
fornece slots para os componentes de alto nível mais comuns do Material Design,
como TopAppBar
,
BottomAppBar
,
FloatingActionButton
,
e Drawer
. Usando
Scaffold
, é fácil garantir que esses componentes estejam posicionados corretamente e
funcionem bem juntos.
@Composable fun HomeScreen(/*...*/) { ModalNavigationDrawer(drawerContent = { /* ... */ }) { Scaffold( topBar = { /*...*/ } ) { contentPadding -> // ... } } }
Recomendados para você
- Observação: o texto do link aparece quando o JavaScript está desativado.
- Modificadores do Compose
- Kotlin para Jetpack Compose
- Componentes e layouts do Material Design