Compose viene con modificadores y elementos componibles integrados para controlar casos de uso comunes de animaciones.
Elementos componibles animados integrados
Anima la apariencia y la desaparición con AnimatedVisibility
![Elemento componible verde que se muestra y se oculta](https://developer.android.google.cn/static/develop/ui/compose/images/animations/animated_visibility_column.gif?authuser=5&hl=es)
El elemento componible AnimatedVisibility
anima la aparición y la desaparición de su contenido.
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 // ... }
De forma predeterminada, el contenido aparece atenuado y expandido, y desaparece desvaneciéndose y achicándose. Puedes personalizar la transición si especificas EnterTransition
y 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 puedes ver en el ejemplo anterior, puedes combinar varios objetos EnterTransition
o ExitTransition
con un operador +
, y cada uno acepta parámetros opcionales para personalizar su comportamiento. Consulta las referencias para obtener más información.
Ejemplos de EnterTransition
y ExitTransition
AnimatedVisibility
también ofrece una variante que toma un MutableTransitionState
. De esta manera, puedes activar una animación en cuanto se agrega AnimatedVisibility
al árbol de la composición. También es útil para observar el estado de la animación.
// 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" } ) }
Cómo animar la entrada y la salida de elementos secundarios
El contenido dentro de AnimatedVisibility
(elementos secundarios directos o indirectos) puede usar el modificador animateEnterExit
a fin de especificar un comportamiento de animación diferente para cada uno de ellos. El efecto visual para cada uno de estos elementos secundarios es una combinación de las animaciones que se especifican en el elemento AnimatedVisibility
que admite composición y las animaciones de entrada y salida propias del elemento secundario.
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… } } }
En algunos casos, es posible que quieras que AnimatedVisibility
no aplique ninguna animación de modo que cada elemento secundario pueda tener sus propias animaciones distintas mediante animateEnterExit
. Para lograrlo, especifica EnterTransition.None
y ExitTransition.None
en el elemento AnimatedVisibility
que admite composición.
Cómo agregar animación personalizada
Si quieres agregar efectos de animación personalizada además de las animaciones de entrada y salida integradas, accede a la instancia Transition
subyacente a través de la propiedad transition
dentro de la lambda de contenido para AnimatedVisibility
. Todos los estados de animación que se agreguen a la instancia Transition se ejecutarán de manera simultánea con las animaciones de entrada y salida de AnimatedVisibility
. AnimatedVisibility
espera hasta que terminen todas las animaciones en Transition
antes de quitar su contenido.
En el caso de las animaciones de salida que se crean independientemente de Transition
(por ejemplo, mediante animate*AsState
), AnimatedVisibility
no podría responder por ellas y, por lo tanto, es posible que quite el contenido que admite composición antes de finalizar.
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)) }
Consulta updateTransition para obtener los detalles sobre Transition
.
Crea animaciones basadas en el estado objetivo con AnimatedContent
El elemento AnimatedContent
que admite composición anima su contenido a medida que cambia en función de un estado objetivo.
Row { var count by remember { mutableStateOf(0) } Button(onClick = { count++ }) { Text("Add") } AnimatedContent(targetState = count) { targetCount -> // Make sure to use `targetCount`, not `count`. Text(text = "Count: $targetCount") } }
Ten en cuenta que siempre debes usar el parámetro lambda y reflejarlo en el contenido. La API usa este valor como clave para identificar el contenido que se muestra en el momento.
De forma predeterminada, el contenido inicial aplica un fundido de salida y, luego, el contenido objetivo aplica un fundido de entrada (este comportamiento se denomina atenuación). Puedes personalizar este comportamiento de animación si especificas un objeto ContentTransform
en el parámetro transitionSpec
. Puedes crear ContentTransform
si combinas EnterTransition
con ExitTransition
mediante la función infija with
. Puedes aplicar SizeTransform
a ContentTransform
si lo adjuntas con la función infija 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() with 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() with slideOutVertically { height -> height } + fadeOut() }.using( // Disable clipping since the faded slide-in/out should // be displayed out of bounds. SizeTransform(clip = false) ) } ) { targetCount -> Text(text = "$targetCount") }
EnterTransition
define cómo debe aparecer el contenido objetivo, y ExitTransition
define cómo debe desaparecer el contenido inicial. Además de todas las funciones EnterTransition
y ExitTransition
disponibles para AnimatedVisibility
, AnimatedContent
ofrece slideIntoContainer
y slideOutOfContainer
.
Estas son alternativas convenientes para slideInHorizontally/Vertically
y slideOutHorizontally/Vertically
que calculan la distancia de la diapositiva según los tamaños del contenido inicial y el contenido objetivo del contenido AnimatedContent
.
SizeTransform
define cómo se debe animar el tamaño entre el contenido inicial y el objetivo. Cuando creas la animación, tienes acceso al tamaño inicial y al tamaño objetivo. SizeTransform
también controla si el contenido debe recortarse según el tamaño del componente durante las animaciones.
var expanded by remember { mutableStateOf(false) } Surface( color = MaterialTheme.colorScheme.primary, onClick = { expanded = !expanded } ) { AnimatedContent( targetState = expanded, transitionSpec = { fadeIn(animationSpec = tween(150, 150)) with 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 } } } } ) { targetExpanded -> if (targetExpanded) { Expanded() } else { ContentIcon() } } }
Cómo animar transiciones secundarias de entrada y salida
Al igual que AnimatedVisibility
, el modificador animateEnterExit
está disponible dentro de la lambda de contenido de AnimatedContent
. Úsalo para aplicar EnterAnimation
y ExitAnimation
a cada uno de los elementos secundarios directos o indirectos por separado.
Cómo agregar animación personalizada
Al igual que AnimatedVisibility
, el campo transition
está disponible dentro de la lambda de contenido de AnimatedContent
. Úsalo para crear un efecto de animación personalizado que se ejecuta de manera simultánea con la transición AnimatedContent
. Consulta updateTransition para obtener más detalles.
Cómo animar entre dos diseños con Crossfade
Crossfade
anima entre dos diseños con una animación de encadenado. Si alternas el valor que se pasa al parámetro current
, el contenido se cambia con una animación de encadenado.
var currentPage by remember { mutableStateOf("A") } Crossfade(targetState = currentPage) { screen -> when (screen) { "A" -> Text("Page A") "B" -> Text("Page B") } }
Modificadores de animación integrados
Cómo animar los cambios de tamaño del elemento componible con animateContentSize
![Elemento componible verde que anima su cambio de tamaño sin problemas.](https://developer.android.google.cn/static/develop/ui/compose/images/animations/animated_content_size.gif?authuser=5&hl=es)
El modificador animateContentSize
anima un cambio de tamaño.
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 } ) { }
Animaciones de elementos de lista
Si deseas animar los reordenamientos de elementos dentro de una lista o cuadrícula diferida, consulta la documentación de animación de elementos de diseño diferido.
Recomendaciones para ti
- Nota: El texto del vínculo se muestra cuando JavaScript está desactivado
- Animaciones basadas en el valor
- Animaciones en Compose
- Compatibilidad con herramientas de animación {:#tooling}