Narzędzie Compose ma wiele wbudowanych mechanizmów animacji i wybór jednego z nich może być przytłaczający. Poniżej znajdziesz listę typowych zastosowań animacji. Więcej informacji o dostępnych opcjach interfejsu API znajdziesz w pełnej dokumentacji tworzenia animacji.
Animuj wspólne właściwości elementów kompozycyjnych
Compose zapewnia wygodne interfejsy API, które pozwalają rozwiązać wiele typowych przypadków użycia animacji. W tej sekcji pokazujemy, jak animować typowe właściwości elementu kompozycyjnego.
animacja pojawiania się i znikania,

Aby ukryć lub wyświetlić kompozyt, użyj AnimatedVisibility
. Dzieci w AnimatedVisibility
mogą używać Modifier.animateEnterExit()
do własnych przejść do i z okna.
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 // ... }
Parametry wejścia i wyjścia AnimatedVisibility
umożliwiają skonfigurowanie sposobu działania elementu kompozycyjnego podczas jego pojawiania się i znikania. Więcej informacji znajdziesz w pełnej dokumentacji.
Inną opcją animowania widoczności składanego jest animowanie przezroczystości w czasie za pomocą 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) ) { }
Zmiana wartości alfa wiąże się jednak z tym, że kompozyt pozostanie w kompozycji i nadal będzie zajmować wyznaczoną mu przestrzeń. Może to spowodować, że czytniki ekranu i inne mechanizmy ułatwień dostępu nadal będą traktować element jako element na ekranie. Z drugiej strony, AnimatedVisibility
ostatecznie usuwa element z kompozycji.

Animowanie koloru tła

val animatedColor by animateColorAsState( if (animateBackgroundColor) colorGreen else colorBlue, label = "color" ) Column( modifier = Modifier.drawBehind { drawRect(animatedColor) } ) { // your composable here }
Ta opcja jest bardziej wydajna niż Modifier.background()
.
Modifier.background()
jest dopuszczalne w przypadku jednorazowego ustawienia koloru, ale podczas animacji koloru w czasie może spowodować więcej przekształceń niż to konieczne.
Informacje o animowaniu koloru tła w nieskończoności znajdziesz w sekcji powtarzanie animacji.
Animuj rozmiar elementu kompozycyjnego

W komponowaniu możesz animować rozmiar komponentów na kilka sposobów. Użyj animateContentSize()
do animacji między zmianami rozmiaru w komponowalnych.
Jeśli na przykład masz pole z tekstem, który może się rozszerzyć z jednego na kilka wierszy, możesz użyć Modifier.animateContentSize()
, aby uzyskać płynniejsze przejście:
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 } ) { }
Możesz też użyć AnimatedContent
z SizeTransform
, aby opisać, jak powinny wyglądać zmiany rozmiaru.
Animowanie pozycji komponentu

Aby animować pozycję składanego, użyj atrybutu Modifier.offset{ }
w połączeniu z atrybutem 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 } )
Jeśli chcesz mieć pewność, że podczas animacji pozycji lub rozmiaru komponenty nie będą nakładać się na inne komponenty, użyj Modifier.layout{ }
. Ten modyfikator rozszerza zmiany rozmiaru i położenia na element nadrzędny, który wpływa na inne elementy podrzędne.
Jeśli np. przenosisz element Box
w obrębie elementu Column
, a inne elementy podrzędne muszą się przemieszczać razem z elementem Box
, uwzględnij informacje o przesunięciu za pomocą parametru Modifier.layout{ }
w ten sposób:
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) ) }

Modifier.layout{ }
Animowanie wypełniania komponentu

Aby animować wypełnienie kompozytowego, użyj atrybutu animateDpAsState
w połączeniu z atrybutem 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 } )
Animowanie wysokości komponentu
Aby animować wysokość komponenta, użyj właściwości animateDpAsState
w połączeniu z właściwością Modifier.graphicsLayer{ }
. W przypadku jednorazowej zmiany wysokości użyj parametru Modifier.shadow()
. Jeśli animujesz cień, bardziej skutecznym rozwiązaniem jest modyfikator Modifier.graphicsLayer{ }
.
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) ) { }
Możesz też użyć komponentu Card
i ustawić dla właściwości elevation różne wartości w zależności od stanu.
Animowanie skali, przesunięcia lub obrotu tekstu

Podczas animowania skali, przesunięcia lub obrotu tekstu ustaw parametr textMotion
w pozycji TextStyle
na TextMotion.Animated
. Dzięki temu przejścia między animacjami tekstu będą płynniejsze. Użyj Modifier.graphicsLayer{ }
, aby obrócić lub skalować tekst.
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) ) }
Animowanie koloru tekstu

Aby animować kolor tekstu, użyj funkcji color
w komponowalnym elemencie 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 }, // ... )
Przełączanie się między różnymi typami treści

Używaj funkcji AnimatedContent
, aby animować różne elementy kompozycyjne. Jeśli chcesz, aby elementy kompozycyjne były zanikać w sposób standardowy, użyj funkcji 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
można dostosować, tak aby wyświetlać wiele różnych rodzajów przejść, Więcej informacji znajdziesz w dokumentacji na stronie AnimatedContent
oraz w tym poście na blogu
AnimatedContent
.
animacje podczas nawigacji do różnych miejsc docelowych.

Aby animować przejścia między składanymi, gdy używasz artefaktu navigation-compose, określ enterTransition
i exitTransition
w składanym. Możesz też ustawić domyślną animację, która będzie używana we wszystkich miejscach docelowych na najwyższym poziomie: 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( // ... ) } }
Istnieje wiele rodzajów przejść, które można stosować do treści wchodzących i wychodzących. Więcej informacji znajdziesz w dokumentacji.
Powtarzanie animacji

Aby animacja była powtarzana w nieskończoność, użyj rememberInfiniteTransition
z infiniteRepeatable
animationSpec
. Zmień RepeatModes
, aby określić, jak ma się przemieszczać.
Użyj funkcji finiteRepeatable
, by powtórzyć określoną liczbę razy.
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 }
Rozpoczęcie animacji podczas uruchamiania komponentu
Narzędzie LaunchedEffect
jest uruchamiane, gdy do kompozycji pojawi się funkcja kompozycyjna. Rozpoczyna animację po uruchomieniu komponentu. Możesz użyć tego do wywołania animacji zmiany stanu. Użycie Animatable
z metodą animateTo
do uruchomienia animacji po uruchomieniu:
val alphaAnimation = remember { Animatable(0f) } LaunchedEffect(Unit) { alphaAnimation.animateTo(1f) } Box( modifier = Modifier.graphicsLayer { alpha = alphaAnimation.value } )
Tworzenie animacji sekwencyjnych

Aby wykonywać animacje sekwencyjne lub równoległe, użyj interfejsów API Animatable
. Wywoływanie funkcji animateTo
w przypadku Animatable
jeden po drugim powoduje, że każda animacja czeka na zakończenie poprzednich animacji, zanim przejdą dalej .
Dzieje się tak, ponieważ jest to funkcja zawieszenia.
val alphaAnimation = remember { Animatable(0f) } val yAnimation = remember { Animatable(0f) } LaunchedEffect("animationKey") { alphaAnimation.animateTo(1f) yAnimation.animateTo(100f) yAnimation.animateTo(500f, animationSpec = tween(100)) }
Tworzenie równoczesnych animacji

Aby uzyskać animacje działające jednocześnie, użyj interfejsów coroutine API (Animatable#animateTo()
lub animate
) albo interfejsu Transition
API. Jeśli w kontekście coroutine używasz wielu funkcji launch, animacje są uruchamiane jednocześnie:
val alphaAnimation = remember { Animatable(0f) } val yAnimation = remember { Animatable(0f) } LaunchedEffect("animationKey") { launch { alphaAnimation.animateTo(1f) } launch { yAnimation.animateTo(100f) } }
Za pomocą interfejsu API updateTransition
możesz używać tego samego stanu do uruchamiania wielu różnych animacji właściwości jednocześnie. W przykładzie poniżej animowane są 2 właściwości kontrolowane przez zmianę stanu: rect
i 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 } }
Optymalizacja działania animacji
Animacje w edytorze mogą powodować problemy z wydajnością. Wynika to z charakteru animacji: przesuwanie lub zmiana pikseli na ekranie w szybkim tempie, z klatka na klatkę, aby stworzyć iluzję ruchu.
Weź pod uwagę różne fazy tworzenia wiadomości: kompozycję, układ i rysowanie. Jeśli animacja zmienia fazę układu, wymaga ponownego rozmieszczenia i narysowania wszystkich dotkniętych kompozytowych komponentów. Jeśli animacja występuje w fazie rysowania, domyślnie będzie działać wydajniej niż w fazie układu, ponieważ będzie miała mniej pracy do wykonania.
Aby mieć pewność, że aplikacja będzie robić jak najmniej animacji podczas tworzenia animacji, w miarę możliwości wybierz wersję lambda funkcji Modifier
. Spowoduje to pominięcie ponownego tworzenia kompozycji i wykonanie animacji poza fazą tworzenia kompozycji. W przeciwnym razie użyj Modifier.graphicsLayer{ }
, ponieważ ten modyfikator zawsze działa w fazie rysowania. Więcej informacji znajdziesz w sekcji odkładanie odczytów w dokumentacji dotyczącej wydajności.
Zmienianie czasu animacji
Domyślnie kompozytor używa animacji elastycznej w przypadku większości animacji. Sprężyny lub animacje oparte na fizyce wydają się bardziej naturalne. Mogą być też przerywane, ponieważ uwzględniają bieżącą prędkość obiektu zamiast stałego czasu.
Jeśli chcesz zastąpić ustawienie domyślne, wszystkie interfejsy API animacji opisane powyżej umożliwiają ustawienie parametru animationSpec
, aby dostosować sposób działania animacji, np. czy ma być wykonywana przez określony czas, czy ma być bardziej dynamiczna.
Oto podsumowanie różnych opcji animationSpec
:
spring
: animacja oparta na fizyce, domyślna dla wszystkich animacji. Możesz zmienić sztywność lub współczynnik tłumienia, aby uzyskać inny wygląd i odczucie animacji.tween
(skrót od between): animacja oparta na czasie trwania, która animuje przejście między 2 wartościami za pomocą funkcjiEasing
.keyframes
: specyfikacja określająca wartości w określonych kluczowych punktach animacji.repeatable
: specyfikacja oparta na czasie, która jest odtwarzana określoną liczbę razy, określoną przezRepeatMode
.infiniteRepeatable
: specyfikacja oparta na czasie trwania, która działa bezterminowo.snap
: automatycznie przyciąga reklamę do wartości końcowej bez żadnej animacji.

Więcej informacji o animationSpecs znajdziesz w pełnej dokumentacji.
Dodatkowe materiały
Więcej przykładów zabawnych animacji w Compose:
- 5 szybkich animacji w sekcji Nowy post
- Poruszanie się wśród meduzy
- Dostosowywanie
AnimatedContent
w sekcji Tworzenie - Funkcje łagodnego przejścia w Compose