Compose ha molti meccanismi di animazione integrati e può essere faticoso saprai quale scegliere. Di seguito è riportato un elenco di casi d'uso comuni relativi alle animazioni. Per informazioni più dettagliate sull'insieme completo delle diverse opzioni API disponibili leggi l'intera documentazione su Compose Animation.
Animazione delle proprietà componibili comuni
Compose offre pratiche API che consentono di risolvere molti dei problemi e i casi d'uso delle animazioni. Questa sezione illustra come puoi animare elementi comuni proprietà di un componibile.
Animazione che appare / scompare
![Composibile verde che mostra e si nasconde](https://developer.android.com/static/develop/ui/compose/images/animations/animated_visibility_column.gif?authuser=9&hl=it)
Utilizza AnimatedVisibility
per nascondere o mostrare un componibile. Bambini all'interno
AnimatedVisibility
può utilizzare Modifier.animateEnterExit()
per il proprio ingresso
o uscire dalla transizione.
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 // ... }
I parametri di entrata e uscita di AnimatedVisibility
consentono di configurare il modo in cui
un componibile si comporta quando appare e scompare. Leggi tutto
documentazione per ulteriori informazioni.
Un'altra opzione per animare la visibilità di un componibile consiste nell'animare il
alpha nel tempo utilizzando animateFloatAsState
:
var visible by remember { mutableStateOf(true) } val animatedAlpha by animateFloatAsState( targetValue = if (visible) 1.0f else 0f, label = "alpha" ) Box( modifier = Modifier .size(200.dp) .graphicsLayer { alpha = animatedAlpha } .clip(RoundedCornerShape(8.dp)) .background(colorGreen) .align(Alignment.TopCenter) ) { }
Tuttavia, la modifica dell'alpha comporta un'avvertenza: l'elemento componibile rimane
della composizione e continua a occupare lo spazio in cui si trova. Questo
potrebbe far sì che gli screen reader e altri meccanismi di accessibilità prendano in considerazione
l'elemento sullo schermo. D'altra parte, AnimatedVisibility
rimuove infine
l'elemento dalla composizione.
![Animazione dell'alfa di un componibile](https://developer.android.com/static/develop/ui/compose/images/animations/animated_visibility_alpha.gif?authuser=9&hl=it)
Anima colore sfondo
![Componibile con colore di sfondo che cambia nel tempo come un'animazione, dove i colori si dissolvono l'uno nell'altro.](https://developer.android.com/static/develop/ui/compose/images/animations/animated_forever.gif?authuser=9&hl=it)
val animatedColor by animateColorAsState( if (animateBackgroundColor) colorGreen else colorBlue, label = "color" ) Column( modifier = Modifier.drawBehind { drawRect(animatedColor) } ) { // your composable here }
Questa opzione è più efficiente rispetto all'utilizzo di Modifier.background()
.
Modifier.background()
è accettabile per un'impostazione di colore one-shot, ma quando
animando un colore nel tempo, il risultato potrebbe essere il numero di ricomposizioni
necessaria.
Per animare all'infinito il colore di sfondo, consulta la sezione sulla ripetizione di un'animazione .
Animazione delle dimensioni di un componibile
![Il componibile verde che anima le sue dimensioni in modo fluido.](https://developer.android.com/static/develop/ui/compose/images/animations/animated_content_size.gif?authuser=9&hl=it)
Compose ti consente di animare le dimensioni dei componibili in diversi modi. Utilizza le funzionalità di
animateContentSize()
per le animazioni tra le modifiche delle dimensioni componibili.
Ad esempio, se hai una casella che contiene del testo che può espandersi da uno a
più linee puoi utilizzare Modifier.animateContentSize()
per ottenere un'immagine più fluida
transizione:
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 } ) { }
Puoi anche utilizzare AnimatedContent
, con un SizeTransform
per descrivere
come devono avvenire le modifiche
delle dimensioni.
Animazione posizione del componibile
![Composizione componibile verde che si anima uniformemente verso il basso e a destra](https://developer.android.com/static/develop/ui/compose/images/animations/animated_offset.gif?authuser=9&hl=it)
Per animare la posizione di un componibile, utilizza Modifier.offset{ }
combinato con
animateIntOffsetAsState()
.
var moved by remember { mutableStateOf(false) } val pxToMove = with(LocalDensity.current) { 100.dp.toPx().roundToInt() } val offset by animateIntOffsetAsState( targetValue = if (moved) { IntOffset(pxToMove, pxToMove) } else { IntOffset.Zero }, label = "offset" ) Box( modifier = Modifier .offset { offset } .background(colorBlue) .size(100.dp) .clickable( interactionSource = remember { MutableInteractionSource() }, indication = null ) { moved = !moved } )
Per fare in modo che i componenti componibili non vengano tracciati sopra o sotto altri
componibili quando si anima posizione o dimensione, utilizza Modifier.layout{ }
. Questo
propagano le modifiche di dimensione e posizione all'elemento principale, il che a sua volta incide
ad altri bambini.
Ad esempio, se stai spostando un Box
all'interno di un Column
e degli altri elementi secondari
devono essere spostati quando si sposta Box
, includi le informazioni sull'offset con
Modifier.layout{ }
come segue:
var toggled by remember { mutableStateOf(false) } val interactionSource = remember { MutableInteractionSource() } Column( modifier = Modifier .padding(16.dp) .fillMaxSize() .clickable(indication = null, interactionSource = interactionSource) { toggled = !toggled } ) { val offsetTarget = if (toggled) { IntOffset(150, 150) } else { IntOffset.Zero } val offset = animateIntOffsetAsState( targetValue = offsetTarget, label = "offset" ) Box( modifier = Modifier .size(100.dp) .background(colorBlue) ) Box( modifier = Modifier .layout { measurable, constraints -> val offsetValue = if (isLookingAhead) offsetTarget else offset.value val placeable = measurable.measure(constraints) layout(placeable.width + offsetValue.x, placeable.height + offsetValue.y) { placeable.placeRelative(offsetValue) } } .size(100.dp) .background(colorGreen) ) Box( modifier = Modifier .size(100.dp) .background(colorBlue) ) }
![2 scatole con il 2° rettangolo che anima la sua posizione X,Y, il terzo reagisce muovendosi anch'esso di un valore Y.](https://developer.android.com/static/develop/ui/compose/images/animations/animated_layout_modifier.gif?authuser=9&hl=it)
Modifier.layout{ }
Animazione della spaziatura interna di un elemento componibile
![Il componibile verde diventa sempre più grande al clic, con la spaziatura interna animata](https://developer.android.com/static/develop/ui/compose/images/animations/animated_padding.gif?authuser=9&hl=it)
Per animare la spaziatura interna di un componibile, utilizza animateDpAsState
combinato con
Modifier.padding()
:
var toggled by remember { mutableStateOf(false) } val animatedPadding by animateDpAsState( if (toggled) { 0.dp } else { 20.dp }, label = "padding" ) Box( modifier = Modifier .aspectRatio(1f) .fillMaxSize() .padding(animatedPadding) .background(Color(0xff53D9A1)) .clickable( interactionSource = remember { MutableInteractionSource() }, indication = null ) { toggled = !toggled } )
Animazione dell'elevazione di un componibile
Per animare l'elevazione di un componibile, utilizza animateDpAsState
combinato con
Modifier.graphicsLayer{ }
. Per i dislivelli occasionali, utilizza
Modifier.shadow()
. Per animare l'ombra, utilizza
Il modificatore Modifier.graphicsLayer{ }
è l'opzione più efficiente.
val mutableInteractionSource = remember { MutableInteractionSource() } val pressed = mutableInteractionSource.collectIsPressedAsState() val elevation = animateDpAsState( targetValue = if (pressed.value) { 32.dp } else { 8.dp }, label = "elevation" ) Box( modifier = Modifier .size(100.dp) .align(Alignment.Center) .graphicsLayer { this.shadowElevation = elevation.value.toPx() } .clickable(interactionSource = mutableInteractionSource, indication = null) { } .background(colorGreen) ) { }
In alternativa, utilizza il componibile Card
e imposta la proprietà di altitudine su
diversi per stato.
Anima la scala, la traduzione o la rotazione del testo
![Testo componibile con scritto](https://developer.android.com/static/develop/ui/compose/images/animations/animated_text.gif?authuser=9&hl=it)
Quando anima la scala, la traslazione o la rotazione del testo, imposta textMotion
su TextStyle
a TextMotion.Animated
. In questo modo avrai una maggiore fluidità
transizioni tra animazioni di testo. Utilizza Modifier.graphicsLayer{ }
per
tradurre, ruotare o scalare il testo.
val infiniteTransition = rememberInfiniteTransition(label = "infinite transition") val scale by infiniteTransition.animateFloat( initialValue = 1f, targetValue = 8f, animationSpec = infiniteRepeatable(tween(1000), RepeatMode.Reverse), label = "scale" ) Box(modifier = Modifier.fillMaxSize()) { Text( text = "Hello", modifier = Modifier .graphicsLayer { scaleX = scale scaleY = scale transformOrigin = TransformOrigin.Center } .align(Alignment.Center), // Text composable does not take TextMotion as a parameter. // Provide it via style argument but make sure that we are copying from current theme style = LocalTextStyle.current.copy(textMotion = TextMotion.Animated) ) }
Anima colore testo
![Le parole](https://developer.android.com/static/develop/ui/compose/images/animations/animated_text_color.gif?authuser=9&hl=it)
Per animare il colore del testo, utilizza la funzione lambda color
nell'elemento componibile BasicText
:
val infiniteTransition = rememberInfiniteTransition(label = "infinite transition") val animatedColor by infiniteTransition.animateColor( initialValue = Color(0xFF60DDAD), targetValue = Color(0xFF4285F4), animationSpec = infiniteRepeatable(tween(1000), RepeatMode.Reverse), label = "color" ) BasicText( text = "Hello Compose", color = { animatedColor }, // ... )
Passare da un tipo di contenuto all'altro
![Schermo verde che dice](https://developer.android.com/static/develop/ui/compose/images/animations/animated_content_slower.gif?authuser=9&hl=it)
Utilizza AnimatedContent
per eseguire l'animazione tra diversi componibili, se
vuoi solo una dissolvenza standard tra i componenti componibili, usa Crossfade
.
var state by remember { mutableStateOf(UiState.Loading) } AnimatedContent( state, transitionSpec = { fadeIn( animationSpec = tween(3000) ) togetherWith fadeOut(animationSpec = tween(3000)) }, modifier = Modifier.clickable( interactionSource = remember { MutableInteractionSource() }, indication = null ) { state = when (state) { UiState.Loading -> UiState.Loaded UiState.Loaded -> UiState.Error UiState.Error -> UiState.Loading } }, label = "Animated Content" ) { targetState -> when (targetState) { UiState.Loading -> { LoadingScreen() } UiState.Loaded -> { LoadedScreen() } UiState.Error -> { ErrorScreen() } } }
AnimatedContent
può essere personalizzato per mostrare molti tipi diversi di immissione e
transizioni di uscita. Per ulteriori informazioni, leggi la documentazione su
AnimatedContent
oppure leggi questo post del blog su
AnimatedContent
Crea animazioni durante la navigazione verso destinazioni diverse
![Due componibili, uno verde che indica Landing e un blu che dice Dettaglio, che si animano facendo scorrere il dettaglio componibile sul componibile di atterraggio.](https://developer.android.com/static/develop/ui/compose/images/animations/navigation_compose_animation.gif?authuser=9&hl=it)
Per animare le transizioni tra gli elementi componibili quando utilizzi i comandi
navigation-compose, specifica enterTransition
e
exitTransition
su un componibile. Puoi anche impostare l'animazione predefinita in modo che
utilizzata per tutte le destinazioni di primo livello NavHost
:
val navController = rememberNavController() NavHost( navController = navController, startDestination = "landing", enterTransition = { EnterTransition.None }, exitTransition = { ExitTransition.None } ) { composable("landing") { ScreenLanding( // ... ) } composable( "detail/{photoUrl}", arguments = listOf(navArgument("photoUrl") { type = NavType.StringType }), enterTransition = { fadeIn( animationSpec = tween( 300, easing = LinearEasing ) ) + slideIntoContainer( animationSpec = tween(300, easing = EaseIn), towards = AnimatedContentTransitionScope.SlideDirection.Start ) }, exitTransition = { fadeOut( animationSpec = tween( 300, easing = LinearEasing ) ) + slideOutOfContainer( animationSpec = tween(300, easing = EaseOut), towards = AnimatedContentTransitionScope.SlideDirection.End ) } ) { backStackEntry -> ScreenDetails( // ... ) } }
Esistono molti tipi diversi di transizioni di entrata e uscita che si applicano effetti diversi sui contenuti in entrata e in uscita, vedi le documentazione.
Ripetere un'animazione
![Uno sfondo verde che si trasforma in uno sfondo blu, con animazioni all'infinito tra i due colori.](https://developer.android.com/static/develop/ui/compose/images/animations/animated_forever.gif?authuser=9&hl=it)
Usa rememberInfiniteTransition
con infiniteRepeatable
animationSpec
per ripetere continuamente l'animazione. Cambia RepeatModes
in
e specificare come deve andare avanti e indietro.
Usa finiteRepeatable
per ripetere un determinato numero di volte.
val infiniteTransition = rememberInfiniteTransition(label = "infinite") val color by infiniteTransition.animateColor( initialValue = Color.Green, targetValue = Color.Blue, animationSpec = infiniteRepeatable( animation = tween(1000, easing = LinearEasing), repeatMode = RepeatMode.Reverse ), label = "color" ) Column( modifier = Modifier.drawBehind { drawRect(color) } ) { // your composable here }
Avvia un'animazione all'avvio di un componibile
LaunchedEffect
viene eseguito quando un componibile entra nella composizione. Inizia
un'animazione all'avvio di un componibile, puoi utilizzarla per generare l'animazione
la modifica dello stato. Utilizza Animatable
con il metodo animateTo
per avviare
animazione al lancio:
val alphaAnimation = remember { Animatable(0f) } LaunchedEffect(Unit) { alphaAnimation.animateTo(1f) } Box( modifier = Modifier.graphicsLayer { alpha = alphaAnimation.value } )
Creare animazioni sequenziali
![Quattro cerchi con frecce verdi che si animano tra un cerchio e l'altro, uno dopo l'altro.](https://developer.android.com/static/develop/ui/compose/images/animations/multiple_properties_sequential.gif?authuser=9&hl=it)
Utilizza le API di coroutine Animatable
per eseguire operazioni sequenziali o simultanee
le animazioni. Chiamata a animateTo
su Animatable
una dopo le altre cause
ogni animazione per attendere il completamento delle animazioni precedenti prima di procedere .
Il motivo è che si tratta di una funzione di sospensione.
val alphaAnimation = remember { Animatable(0f) } val yAnimation = remember { Animatable(0f) } LaunchedEffect("animationKey") { alphaAnimation.animateTo(1f) yAnimation.animateTo(100f) yAnimation.animateTo(500f, animationSpec = tween(100)) }
Crea animazioni simultanee
![Tre cerchi con frecce verdi che si animano su ognuno di essi, che si animano tutti insieme contemporaneamente.](https://developer.android.com/static/develop/ui/compose/images/animations/multiple_properties.gif?authuser=9&hl=it)
Utilizzare le API Coroutine (Animatable#animateTo()
o animate
) oppure
l'API Transition
per ottenere animazioni simultanee. Se utilizzi più
funzioni in un contesto di coroutine, lancia le animazioni allo stesso
volta:
val alphaAnimation = remember { Animatable(0f) } val yAnimation = remember { Animatable(0f) } LaunchedEffect("animationKey") { launch { alphaAnimation.animateTo(1f) } launch { yAnimation.animateTo(100f) } }
Puoi usare l'API updateTransition
per utilizzare lo stesso stato per guidare
molte animazioni di proprietà diverse contemporaneamente. Nell'esempio riportato di seguito
due proprietà controllate da un cambiamento di stato, rect
e borderWidth
:
var currentState by remember { mutableStateOf(BoxState.Collapsed) } val transition = updateTransition(currentState, label = "transition") val rect by transition.animateRect(label = "rect") { state -> when (state) { BoxState.Collapsed -> Rect(0f, 0f, 100f, 100f) BoxState.Expanded -> Rect(100f, 100f, 300f, 300f) } } val borderWidth by transition.animateDp(label = "borderWidth") { state -> when (state) { BoxState.Collapsed -> 1.dp BoxState.Expanded -> 0.dp } }
Ottimizza le prestazioni delle animazioni
Le animazioni in Compose possono causare problemi di prestazioni. Ciò è dovuto alla natura che cos'è un'animazione: spostamento o modifica rapidi di pixel sullo schermo, fotogramma per fotogramma per creare l'illusione di movimento.
Considera le diverse fasi di Scrittura: composizione, layout e disegno. Se l'animazione cambia la fase di layout, richiede che tutti gli elementi componibili interessati e ridisegno. Se l'animazione si verifica nella fase di disegno, per impostazione predefinita avranno prestazioni più elevate rispetto a quando eseguirai l'animazione nel layout perché richiederebbe nel complesso meno lavoro da fare.
Per assicurarti che l'app esegua il meno possibile durante l'animazione, scegli il comando lambda
di un dispositivo Modifier
, ove possibile. In questo modo viene saltata la ricomposizione e viene eseguita
l'animazione al di fuori della fase di composizione, altrimenti usa
Modifier.graphicsLayer{ }
, perché questo modificatore è sempre incluso nell'estrazione
durante la fase di sviluppo. Per ulteriori informazioni, consulta la sezione Posticipare letture in
la documentazione sulle prestazioni.
Modificare la durata dell'animazione
Per impostazione predefinita, la funzionalità Scrivi utilizza le animazioni primaverili per la maggior parte delle animazioni. Springs o
animazioni basate sulla fisica, risultano più naturali. Sono inoltre interrompibili perché
prendono in considerazione la velocità attuale dell'oggetto, invece che un tempo fisso.
Se vuoi eseguire l'override dell'impostazione predefinita, tutte le API di animazione illustrate sopra
puoi impostare un animationSpec
per personalizzare la modalità di esecuzione di un'animazione,
che venga eseguita durante un certo periodo di tempo o sia più scorrevole.
Di seguito è riportato un riepilogo delle diverse opzioni di animationSpec
:
spring
: animazione basata sulla fisica, l'impostazione predefinita per tutte le animazioni. Tu è possibile modificare la rigidità o il rapporto di smorzamento per ottenere una diversa animazione aspetto e design.tween
(abbreviazione di tra): animazione basata sulla durata, si anima tra due valori con una funzioneEasing
.keyframes
: specifica per specificare i valori in determinati punti chiave in una l'animazione.repeatable
: specifica basata sulla durata eseguita un certo numero di volte. specificato daRepeatMode
.infiniteRepeatable
: specifica basata sulla durata eseguita all'infinito.snap
: si aggancia istantaneamente al valore finale senza alcuna animazione.
![Scrivi il testo alternativo qui](https://developer.android.com/static/develop/ui/compose/images/animations/animated_spec_set.gif?authuser=9&hl=it)
Leggi la documentazione completa per ulteriori informazioni su animationSpecs.
Risorse aggiuntive
Per altri esempi di animazioni divertenti in Compose, dai un'occhiata a quanto segue:
- 5 animazioni rapide in Compose
- Spostare le meduse in Compose
- Personalizzazione di
AnimatedContent
in Compose - Accesso alle funzioni di easing in Compose