O Compose vem com elementos combináveis e modificadores integrados para lidar com casos de uso de animação comuns.
Elementos combináveis animados integrados
Animar a aparição e o desaparecimento com AnimatedVisibility
A função
AnimatedVisibility
que pode ser composta anima o aparecimento e desaparecimento de conteúdo.
var visible by remember { mutableStateOf(true) } // Animated visibility will eventually remove the item from the composition once the animation has finished. AnimatedVisibility(visible) { // your composable here // ... }
Por padrão, o aparecimento do conteúdo ocorre com esmaecimento e expansão e o desaparecimento
com esmaecimento e encolhimento. A transição pode ser personalizada especificando
EnterTransition
e
ExitTransition
.
var visible by remember { mutableStateOf(true) } val density = LocalDensity.current AnimatedVisibility( visible = visible, enter = slideInVertically { // Slide in from 40 dp from the top. with(density) { -40.dp.roundToPx() } } + expandVertically( // Expand from the top. expandFrom = Alignment.Top ) + fadeIn( // Fade in with the initial alpha of 0.3f. initialAlpha = 0.3f ), exit = slideOutVertically() + shrinkVertically() + fadeOut() ) { Text( "Hello", Modifier .fillMaxWidth() .height(200.dp) ) }
Como mostrado no exemplo acima, é possível combinar vários objetos EnterTransition
ou ExitTransition
com um operador +
, sendo que cada um aceita parâmetros opcionais
para personalizar o comportamento. Consulte as referências para mais informações.
Exemplos da EnterTransition
e ExitTransition
A função de composição AnimatedVisibility
também oferece uma variante que recebe uma classe
MutableTransitionState
. Isso permite que você acione uma animação assim que a
AnimatedVisibility
for adicionada à árvore de composição. Também é útil para
observar o estado da animação.
// Create a MutableTransitionState<Boolean> for the AnimatedVisibility. val state = remember { MutableTransitionState(false).apply { // Start the animation immediately. targetState = true } } Column { AnimatedVisibility(visibleState = state) { Text(text = "Hello, world!") } // Use the MutableTransitionState to know the current animation state // of the AnimatedVisibility. Text( text = when { state.isIdle && state.currentState -> "Visible" !state.isIdle && state.currentState -> "Disappearing" state.isIdle && !state.currentState -> "Invisible" else -> "Appearing" } ) }
Animar a entrada e a saída de filhas
O conteúdo em AnimatedVisibility
(animações filhas diretas ou indiretas) pode usar o modificador
animateEnterExit
para especificar um comportamento de animação diferente para cada uma delas. O efeito
visual de cada uma dessas filhas é uma combinação das animações especificadas
na função AnimatedVisibility
e nas animações
de entrada e saída das próprias filhas.
var visible by remember { mutableStateOf(true) } AnimatedVisibility( visible = visible, enter = fadeIn(), exit = fadeOut() ) { // Fade in/out the background and the foreground. Box( Modifier .fillMaxSize() .background(Color.DarkGray) ) { Box( Modifier .align(Alignment.Center) .animateEnterExit( // Slide in/out the inner box. enter = slideInVertically(), exit = slideOutVertically() ) .sizeIn(minWidth = 256.dp, minHeight = 64.dp) .background(Color.Red) ) { // Content of the notification… } } }
Em alguns casos, pode ser necessário fazer com que AnimatedVisibility
não aplique animações
para que cada filha possa ter animações diferentes usando
animateEnterExit
. Para fazer isso, especifique EnterTransition.None
e
ExitTransition.None
na função AnimatedVisibility
que pode ser composta.
Adicionar uma animação personalizada
Se você quiser adicionar efeitos de animação personalizados, além das animações de entrada e saída
integradas, acesse a instância Transition
usando a propriedade transition
dentro da lambda de conteúdo da AnimatedVisibility
. Todos os estados
de animação adicionados à instância de transição serão executados simultaneamente com as animações
de entrada e saída de AnimatedVisibility
. A AnimatedVisibility
aguarda até que
todas as animações em Transition
tenham terminado antes de remover o conteúdo.
A AnimatedVisibility
não consegue considerar animações de saída criadas de forma
independente da Transition
, como o uso de
animate*AsState
. Por isso, é possível que o elemento do conteúdo seja removido antes do fim da transição.
var visible by remember { mutableStateOf(true) } AnimatedVisibility( visible = visible, enter = fadeIn(), exit = fadeOut() ) { // this: AnimatedVisibilityScope // Use AnimatedVisibilityScope#transition to add a custom animation // to the AnimatedVisibility. val background by transition.animateColor(label = "color") { state -> if (state == EnterExitState.Visible) Color.Blue else Color.Gray } Box( modifier = Modifier .size(128.dp) .background(background) ) }
Consulte updateTransition para saber mais sobre a Transition
.
Animar com base no estado de destino com AnimatedContent
O elemento AnimatedContent
combinável anima o conteúdo de acordo com um
estado de destino.
Row { var count by remember { mutableIntStateOf(0) } Button(onClick = { count++ }) { Text("Add") } AnimatedContent( targetState = count, label = "animated content" ) { targetCount -> // Make sure to use `targetCount`, not `count`. Text(text = "Count: $targetCount") } }
Use sempre o parâmetro lambda e o reflita no conteúdo. A API usa esse valor como a chave para identificar o conteúdo que está sendo mostrado.
Por padrão, o conteúdo inicial esmaece e o conteúdo de destino aparece gradualmente.
Esse comportamento é chamado de esmaecimento gradual (link em inglês). É
possível personalizar esse comportamento de animação especificando um objeto ContentTransform
para o parâmetro
transitionSpec
. Você pode criar um ContentTransform
combinando um
EnterTransition
com um ExitTransition
,
usando a função de infixo with
. Aplique uma SizeTransform
ao ContentTransform
anexando a função de infixo
using
.
AnimatedContent( targetState = count, transitionSpec = { // Compare the incoming number with the previous number. if (targetState > initialState) { // If the target number is larger, it slides up and fades in // while the initial (smaller) number slides up and fades out. slideInVertically { height -> height } + fadeIn() togetherWith slideOutVertically { height -> -height } + fadeOut() } else { // If the target number is smaller, it slides down and fades in // while the initial number slides down and fades out. slideInVertically { height -> -height } + fadeIn() togetherWith slideOutVertically { height -> height } + fadeOut() }.using( // Disable clipping since the faded slide-in/out should // be displayed out of bounds. SizeTransform(clip = false) ) }, label = "animated content" ) { targetCount -> Text(text = "$targetCount") }
O objeto EnterTransition
define como o conteúdo de destino vai aparecer, e
ExitTransition
define como o conteúdo inicial vai desaparecer. Além de
todas as funções de EnterTransition
e ExitTransition
disponíveis para
AnimatedVisibility
, AnimatedContent
oferece slideIntoContainer
e slideOutOfContainer
.
Essas são alternativas convenientes a slideInHorizontally/Vertically
e
slideOutHorizontally/Vertically
, que calculam a distância do deslizamento com base
nos tamanhos do conteúdo inicial e do conteúdo de destino do
AnimatedContent
.
SizeTransform
define como o
tamanho será animado entre o conteúdo inicial e de destino. Ao criar
a animação, você tem acesso ao tamanho inicial e ao tamanho
de destino. SizeTransform
também controla se o conteúdo precisa ser cortado
para o tamanho do componente durante as animações.
var expanded by remember { mutableStateOf(false) } Surface( color = MaterialTheme.colorScheme.primary, onClick = { expanded = !expanded } ) { AnimatedContent( targetState = expanded, transitionSpec = { fadeIn(animationSpec = tween(150, 150)) togetherWith fadeOut(animationSpec = tween(150)) using SizeTransform { initialSize, targetSize -> if (targetState) { keyframes { // Expand horizontally first. IntSize(targetSize.width, initialSize.height) at 150 durationMillis = 300 } } else { keyframes { // Shrink vertically first. IntSize(initialSize.width, targetSize.height) at 150 durationMillis = 300 } } } }, label = "size transform" ) { targetExpanded -> if (targetExpanded) { Expanded() } else { ContentIcon() } } }
Animar transições de entrada e saída de filhas
Assim como AnimatedVisibility
, o modificador animateEnterExit
está disponível dentro da lambda de conteúdo de AnimatedContent
. Use esse
modificador para aplicar o EnterAnimation
e o ExitAnimation
a cada um dos filhos diretos ou indiretos
separadamente.
Adicionar uma animação personalizada
Assim como AnimatedVisibility
, o campo transition
está disponível dentro da
lambda de conteúdo de AnimatedContent
. Use esse campo para criar um efeito de animação
personalizado que será executado simultaneamente com a transição AnimatedContent
. Consulte
updateTransition para saber mais.
Animar entre dois layouts com Crossfade
O Crossfade
é animado entre dois layouts com uma animação de fading cruzado. Ao alternar
o valor transmitido para o parâmetro current
, o conteúdo muda com uma
animação de fading cruzado.
var currentPage by remember { mutableStateOf("A") } Crossfade(targetState = currentPage, label = "cross fade") { screen -> when (screen) { "A" -> Text("Page A") "B" -> Text("Page B") } }
Modificadores de animação integrados
Animar mudanças no tamanho do elemento combinável com animateContentSize
O modificador animateContentSize
anima uma mudança de tamanho.
var expanded by remember { mutableStateOf(false) } Box( modifier = Modifier .background(colorBlue) .animateContentSize() .height(if (expanded) 400.dp else 200.dp) .fillMaxWidth() .clickable( interactionSource = remember { MutableInteractionSource() }, indication = null ) { expanded = !expanded } ) { }
Animações de itens de lista
Se você quiser animar a reordenação dos itens em uma lista ou grade lenta, consulte a documentação de animação de itens de layout lento.
Recomendados para você
- Observação: o texto do link aparece quando o JavaScript está desativado.
- Animações com base no valor
- Animações no Compose
- Suporte a ferramentas de animação {:#tooling}