1. Antes de começar
Neste codelab, você vai aprender a adicionar uma animação simples ao seu app Android. Animações podem tornar o app mais interativo, interessante e fácil de interpretar para os usuários. Animar atualizações individuais em uma tela cheia de informações pode ajudar o usuário a notar o que mudou.
Há muitos tipos de animações que podem ser usados na interface do usuário de um app. Os itens podem esmaecer à medida que aparecem ou desaparecem, podem ser movidos para dentro ou para fora da tela ou ser transformados de maneiras interessantes. Isso ajuda a tornar a IU do app expressiva e fácil de usar.
As animações também dão um visual sofisticado ao app, o que proporciona uma aparência elegante e ajuda o usuário:
As animações que recompensam o usuário por uma tarefa podem tornar os momentos importantes da jornada do usuário mais significativos. | |
Os elementos animados que respondem à entrada do teclado oferecem feedback para mostrar se a ação foi bem-sucedida. | |
Os itens de listas animadas são marcadores que informam que o conteúdo está sendo carregado. | |
Uma ação animada, como deslizar para abrir, incentiva gestos. | |
Ícones animados podem complementar o significado do ícone. |
Pré-requisitos
- Conhecimento sobre Kotlin, incluindo funções, lambdas e composições sem estado.
- Conhecimento básico sobre como criar layouts no Jetpack Compose.
- Conhecimento básico sobre como criar listas no Jetpack Compose.
- Conhecimento básico do Material Design.
O que você vai aprender
- Como criar uma animação de mola simples com o Jetpack Compose.
O que você vai criar
- Você vai usar o app Woof do codelab "Temas do Material Design" com o Jetpack Compose e adicionar uma animação simples para reconhecer a ação do usuário.
O que é necessário
- Ter a versão mais recente do Android Studio.
- Conexão de Internet para fazer o download do código inicial.
2. Visão geral do app
No codelab "Temas do Material Design" com o Jetpack Compose, você usou o Material Design para criar o app Woof, que mostra uma lista de cachorros e as informações de cada um.
Neste codelab, você vai adicionar uma animação ao app Woof com informações sobre hobbies que vão ser mostrados quando você expandir o item da lista. Você também vai adicionar uma animação de mola para animar o item da lista que está sendo expandido:
Acessar o código inicial
Para começar, faça o download do código inicial:
Outra opção é clonar o repositório do GitHub:
$ git clone https://github.com/google-developer-training/basic-android-kotlin-compose-training-woof.git $ cd basic-android-kotlin-compose-training-woof $ git checkout material
Procure o código no repositório do GitHub do Woof app
(link em inglês).
3. Adicionar ícone de "Expandir mais"
O primeiro passo para criar uma animação de mola é adicionar um ícone de "Expandir mais" . O ícone de expandir mais fornece um botão para que o usuário expanda o item da lista.
Ícones
Ícones são símbolos que ajudam os usuários a entender a interface com uma comunicação visual da função pretendida, inspirada em objetos do mundo físico. Muitas vezes, o design do ícone reduz o nível de detalhes para o mínimo necessário para ser facilmente reconhecido pelo usuário. Por exemplo, um lápis no mundo físico é usado para escrever. Portanto, o ícone correspondente geralmente indica criar ou editar.
|
O Material Design oferece vários ícones organizados em categorias comuns para a maioria das suas necessidades.
Adicionar dependência do Gradle
Adicione a dependência da biblioteca material-icons-extended
ao seu projeto. Você vai usar os ícones Icons.Filled.ExpandLess
e
Icons.Filled.ExpandMore
desta biblioteca.
- No painel do projeto, abra Gradle Scripts > build.gradle (Module: Woof.app).
- Role até o fim do arquivo
build.gradle (Module: Woof.app)
. No blocodependencies{}
, adicione esta linha:
implementation "androidx.compose.material:material-icons-extended:$compose_version"
Adicionar o ícone de composição
Adicione uma função para mostrar o ícone de expandir da biblioteca de ícones do Material Design e use-a como um botão.
- Em
MainActivity.kt
, após a funçãoDogItem()
, crie uma nova função de composição com o nomeDogItemButton()
. - Transmita um
Boolean
para o estado expandido, uma expressão lambda para o evento de clique do botão e umModifier
opcional, desta forma:
@Composable
private fun DogItemButton(
expanded: Boolean,
onClick: () -> Unit,
modifier: Modifier = Modifier
) {
}
- Na função
DogItemButton()
, adicione uma função de composiçãoIconButton()
que aceite um parâmetro com o nomeonClick
, uma expressão lambda usando a sintaxe de lambda final, que é invocada quando esse ícone é pressionado e defina a lambda no argumento transmitido emonClick
.
@Composable
private fun DogItemButton(
// ...
) {
IconButton(onClick = onClick) {
}
}
- No bloco lambda
IconButton()
, adicione um elemento de composiçãoIcon
com um parâmetro com o nomeimageVector
e o defina comoIcons.Filled.ExpandMore
. Este é o botão de íconeque vai aparecer no fim do item da lista. O Android Studio mostra um aviso para os parâmetros de composição
Icon()
que você vai corrigir nas próximas etapas. - Adicione o parâmetro com o nome
tint
e defina a cor do ícone comoMaterialTheme.colors.secondary
. Adicione o parâmetro com o nomecontentDescription
e o defina como o recurso de stringR.string.expand_button_content_description
.
import androidx.compose.material.Icon
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ExpandMore
IconButton(onClick = onClick) {
Icon(
imageVector = Icons.Filled.ExpandMore,
tint = MaterialTheme.colors.secondary,
contentDescription = stringResource(R.string.expand_button_content_description)
)
}
Mostrar o ícone
Adicione o elemento de composição DogItemButton()
ao layout para mostrá-lo.
- No início da função de composição
DogItem()
, adicione umvar
para salvar o estado expandido do item da lista. Defina o valor inicial comofalse
.
var expanded by remember { mutableStateOf(false) }
- No final do bloco
Row
da função de composiçãoDogItem()
, chame a funçãoDogItemButton()
e transmita um estado expandido e uma expressão lambda vazia ao callback. Esse código mostra o botão de ícone no item da lista. - Para mostrar o botão de ícone no item da lista, na função de composição
DogItem()
ao final do blocoRow
, após uma chamada paraDogInformation()
, faça uma chamada para oDogItemButton()
. Transmita o estadoexpanded
e uma expressão lambda vazia ao callback. Você vai definir essa função lambda em uma etapa posterior.
Row(
modifier = Modifier
.fillMaxWidth()
.padding(8.dp)
) {
DogIcon(dog.imageResourceId)
DogInformation(dog.name, dog.age)
DogItemButton(
expanded = expanded,
onClick = { }
)
}
- Crie e atualize a visualização no painel Design.
O botão "Expandir mais" não está alinhado ao fim do item da lista. Isso vai ser corrigido na próxima etapa.
Alinhar o botão "Expandir mais"
Para alinhar o botão "Expandir mais" ao fim do item da lista, é necessário adicionar um espaçador ao layout com o atributo Modifier.weight()
.
No app Woof, cada linha de itens da lista contém uma imagem e informações de um cachorro, além de um botão "Expandir mais". Você vai adicionar um espaçador de composição antes do botão "Expandir mais" com o peso 1f
para alinhar corretamente o ícone do botão. Como o espaçador é o único elemento filho com peso na linha, ele preenche o espaço restante depois de medir o comprimento do outro elemento filho que não tem um peso.
Adicionar o espaçador à linha do item da lista
- No final do bloco
Row
na função de composiçãoDogItem()
, adicione umSpacer
. Transmita umModifier
comweight(1f)
. OModifier.weight()
faz com que o espaçador ocupe o espaço restante na linha.
Row(
modifier = Modifier
.fillMaxWidth()
.padding(8.dp)
) {
DogIcon(dog.imageResourceId)
DogInformation(dog.name, dog.age)
Spacer(Modifier.weight(1f))
DogItemButton(
expanded = expanded,
onClick = { }
)
}
- Crie e atualize a visualização no painel Design. O botão "Expandir mais" agora está alinhado ao fim do item da lista.
4. Adicionar uma função de composição para mostrar um hobby
Nesta tarefa, você vai adicionar os elementos Text
de composição para mostrar as informações de hobbies do cachorro.
- Crie uma nova função de composição com o nome
DogHobby()
, que usa um ID de recurso de string de hobby de um cachorro e umModifier
opcional. - Na função
DogHobby()
, crie uma coluna com os seguintes atributos de padding para adicionar espaço entre a coluna e os elementos de composição filhos.
import androidx.annotation.StringRes
@Composable
fun DogHobby(@StringRes dogHobby: Int, modifier: Modifier = Modifier) {
Column(
modifier = modifier.padding(
start = 16.dp,
top = 8.dp,
bottom = 16.dp,
end = 16.dp
)
) { }
}
- Dentro do bloco de colunas, adicione dois elementos de composição
Text
: um para mostrar o texto About (Sobre) acima das informações do hobby e outro para as informações.
- Para o texto About, defina o estilo como
h3
(Título 3) e a cor comoonBackground
. Para as informações sobre hobbies, defina o estilo comobody1
(Corpo 1).
Column(
modifier = modifier.padding(
//..
)
) {
Text(
text = stringResource(R.string.about),
style = MaterialTheme.typography.h3,
)
Text(
text = stringResource(dogHobby),
style = MaterialTheme.typography.body1,
)
}
- A função de composição
DogHobby()
vai ficar assim.
@Composable
fun DogHobby(@StringRes dogHobby: Int, modifier: Modifier = Modifier) {
Column(
modifier = modifier.padding(
start = 16.dp,
top = 8.dp,
bottom = 16.dp,
end = 16.dp
)
) {
Text(
text = stringResource(R.string.about),
style = MaterialTheme.typography.h3
)
Text(
text = stringResource(dogHobby),
style = MaterialTheme.typography.body1
)
}
}
- Para mostrar a função de composição
DogHobby()
, emDogItem()
, una aRow
com umaColumn
. Chame a funçãoDogHobby()
transmitindodog.hobbies
como parâmetro, depois transmita aRow
como o segundo elemento filho.
Column() {
Row(
//..
) {
//..
}
DogHobby(dog.hobbies)
}
A função DogItem()
completa vai ficar assim:
@Composable
fun DogItem(dog: Dog, modifier: Modifier = Modifier) {
var expanded by remember { mutableStateOf(false) }
Card(
elevation = 4.dp,
modifier = modifier.padding(8.dp)
) {
Column() {
Row(
Row(
modifier = Modifier
.fillMaxWidth()
.padding(8.dp)
) {
DogIcon(dog.imageResourceId)
DogInformation(dog.name, dog.age)
Spacer(Modifier.weight(1f))
DogItemButton(
expanded = expanded,
onClick = { expanded = !expanded },
)
}
DogHobby(dog.hobbies)
}
}
}
- Crie e atualize a visualização no painel Design. Observe os hobbies do cachorro.
5. Mostrar ou ocultar o hobby ao clicar no botão
Seu app tem um botão "Expandir mais" para cada item da lista, mas ele ainda não faz nada. Nesta seção, você vai adicionar a opção de ocultar ou revelar as informações de hobbies quando o usuário clicar no botão.
- Na função de composição
DogItem()
, na chamada de funçãoDogItemButton()
, defina a expressão lambdaonClick()
, mude o valor do estado booleanoexpanded
paratrue
quando o botão receber um clique e volte o valor parafalse
se o botão for clicado novamente.
DogItemButton(
expanded = expanded,
onClick = { expanded = !expanded }
)
- Na função
DogItemButton()
, junte a chamada de funçãoDogHobby()
com uma verificaçãoif
no booleanoexpanded
.
// No need to copy over
@Composable
fun DogItem(dog: Dog, modifier: Modifier = Modifier) {
var expanded by remember { mutableStateOf(false) }
Card(
//..
) {
Column() {
Row(
//..
) {
//..
}
if (expanded) {
DogHobby(dog.hobbies)
}
}
}
}
No código acima, as informações de hobbies do cachorro só são mostradas se o valor de expanded
for true
.
- A prévia pode mostrar como a IU vai ficar e também permite interagir com ela. Para interagir com a visualização da IU, clique no botão Modo interativo
no canto direito de cima do painel Design. Isso inicia a visualização no modo interativo.
- Interaja com a visualização clicando no botão "Expandir mais". As informações sobre o hobby ficam ocultas e são reveladas quando você clica no botão "Expandir mais".
O ícone do botão "Expandir mais" continua o mesmo quando o item da lista é expandido. Para uma melhor experiência do usuário, mude o ícone para que ExpandMore
mostre a seta para baixo e
ExpandLess
mostre a seta para cima .
- Na função
DogItemButton()
, atualize o valorimageVector
com base no estadoexpanded
desta maneira:
import androidx.compose.material.icons.filled.ExpandLess
@Composable
private fun DogItemButton(
//..
) {
IconButton(onClick = onClick) {
Icon(
imageVector = if (expanded) Icons.Filled.ExpandLess else Icons.Filled.ExpandMore,
//..
)
}
}
- Execute o app em um dispositivo ou emulador, ou use o modo interativo na visualização novamente. O ícone alterna entre as imagens
ExpandMore
e
ExpandLess
.
Bom trabalho ao atualizar o ícone!
Ao expandir o item da lista, você notou uma mudança repentina na altura? A mudança repentina de altura não dá a impressão de um app sofisticado. Para resolver esse problema, você vai adicionar uma animação ao app.
6. Adicionar animação
As animações podem adicionar dicas visuais que mostram aos usuários o que está acontecendo no app. Elas são especialmente úteis quando a IU muda de estado, por exemplo, quando um novo conteúdo é carregado ou novas ações ficam disponíveis. As animações também podem adicionar um visual sofisticado ao app.
Nesta seção, você vai adicionar uma animação de mola para a mudança na altura do item da lista.
Animação de mola
A animação de mola é baseada na física e impulsionada por uma força elástica. Com ela, o valor e a velocidade do movimento são calculados com base na força aplicada.
Por exemplo, se você arrastar um ícone do app pela tela e o soltar levantando o dedo, ele vai voltar para o local original movido por uma força invisível.
A animação abaixo mostra o efeito de mola. Quando o ícone é solto, ele pula, imitando uma mola.
Efeito de mola
A força de mola é guiada pelas duas propriedades abaixo:
- Proporção de amortecimento: a elasticidade da mola.
- Nível de rigidez: a rigidez da mola, ou seja, a velocidade com que ela se move em direção ao final.
Confira abaixo alguns exemplos de animações com proporções de amortecimento e níveis de rigidez diferentes.
|
|
|
|
Agora, você vai adicionar uma animação de mola ao app.
- Em
MainActivity.kt
, na funçãoDogItem()
, adicione um parâmetromodifier
ao layoutColumn
.
@Composable
fun DogItem(dog: Dog, modifier: Modifier = Modifier) {
//..
Card(
//..
) {
Column(
modifier = Modifier
){
//..
}
}
}
Observe a chamada de função DogHobby()
na função de composição DogItem()
. As informações de hobbies do cachorro são incluídas na composição com base no valor booleano expanded
. A altura do item da lista muda caso as informações de hobbies estejam visíveis ou ocultas. Você vai usar o modificador animateContentSize
para adicionar uma transição entre a altura nova e a antiga.
// No need to copy over
@Composable
fun DogItem(...) {
//..
if (expanded) {
DogHobby(dog.hobbies)
}
}
- Encadeie o modificador com
animateContentSize
para animar a mudança de tamanho (altura do item da lista).
import androidx.compose.animation.animateContentSize
Column(
modifier = Modifier
.animateContentSize()
) {
//..
}
Com a implementação atual, você está animando a altura do item da lista no app. No entanto, a animação é tão sutil que fica difícil perceber quando você executa o app. Para resolver isso, use um parâmetro opcional animationSpec
que permite personalizar a animação.
- Adicione o parâmetro
animationSpec
à chamada de funçãoanimateContentSize()
. Defina-o como uma animação de mola com os parâmetrosDampingRatioMediumBouncy
eStiffnessLow
.
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.spring
Column(
modifier = Modifier
.animateContentSize(
animationSpec = spring(
dampingRatio = Spring.DampingRatioMediumBouncy,
stiffness = Spring.StiffnessLow
)
)
)
- Crie e atualize a visualização no painel Design. Use o modo interativo ou execute seu app em um emulador ou dispositivo para conferir a animação de mola em ação.
Execute novamente o app no emulador ou dispositivo e aproveite o belo app com animações.
7. (Opcional) Fazer experimentos com outras animações
animate*AsState
As funções animate*AsState()
são uma das APIs de animação mais simples do Compose, usadas para animar um único valor. Somente o valor final (ou valor de segmentação) precisa ser informado, e a API inicia a animação do valor atual para o valor especificado.
O Compose oferece funções animate*AsState()
para Float
, Color
, Dp
, Size
, Offset
e Int
, entre outras. Você pode adicionar suporte a outros tipos de dados facilmente com animateValueAsState()
, que usa um tipo genérico.
Use a função animateColorAsState()
para animar a cor quando um item da lista estiver expandido.
Dica:
- Declare uma cor e delegue a inicialização dela à função
animateColorAsState()
. - Defina o parâmetro
targetValue
, dependendo do valor booleanoexpanded
.
@Composable
fun DogItem(dog: Dog, modifier: Modifier = Modifier) {
//..
val color by animateColorAsState(
targetValue = if (expanded) Green25 else MaterialTheme.colors.surface,
)
Card(
//..
) {...}
}
- Defina a
color
declarada acima como o modificador de segundo plano como aColumn
.
@Composable
fun DogItem(dog: Dog, modifier: Modifier = Modifier) {
//..
Card(
//..
) {
Column(
modifier = Modifier
.animateContentSize(
//..
)
)
.background(color = color)
) {...}
}
8. Acessar o código da solução
Para fazer o download do código do codelab concluído, use este comando git:
$ git clone https://github.com/google-developer-training/basic-android-kotlin-compose-training-woof.git
Se preferir, você pode fazer o download do repositório como um arquivo ZIP, descompactar e abrir no Android Studio.
Se você quiser conferir o código da solução, acesse o GitHub (link em inglês).
9. Conclusão
Parabéns! Você adicionou um botão para ocultar e mostrar informações sobre o cachorro. Você melhorou a experiência do usuário usando animações de mola. Você também aprendeu a usar o modo interativo no painel de Design.
Você também pode testar um tipo diferente de animação do Jetpack Compose. Não se esqueça de compartilhar seu trabalho nas redes sociais com a hashtag #AndroidBasics.
Saiba mais
- Animação do Jetpack Compose
- Animação de mola do Jetpack Compose
- Codelab: Como animar elementos no Jetpack Compose
- Vídeo: Animação reimaginada (em inglês)
- Vídeo: Jetpack Compose: animação (em inglês)