Compose bietet viele integrierte Animationsmechanismen und es kann schwierig sein, den richtigen auszuwählen. Im Folgenden finden Sie eine Liste gängiger Anwendungsfälle für Animationen. Weitere Informationen zu den verschiedenen verfügbaren API-Optionen findest du in der vollständigen Dokumentation zur Erstellung von Animationen.
Gängige zusammensetzbare Eigenschaften animieren
Compose bietet praktische APIs, mit denen sich viele gängige Anwendungsfälle für Animationen lösen lassen. In diesem Abschnitt wird gezeigt, wie Sie gängige Eigenschaften eines Composeables animieren können.
Ein-/Ausblenden animieren
Mit AnimatedVisibility
kannst du ein Composeable aus- oder einblenden. Untergeordnete Elemente von AnimatedVisibility
können Modifier.animateEnterExit()
für ihren eigenen Eintritts- oder Ausstiegsübergang verwenden.
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 // ... }
Mit den Parametern „enter“ und „exit“ von AnimatedVisibility
können Sie konfigurieren, wie sich ein Composeable beim Ein- und Ausblenden verhält. Weitere Informationen finden Sie in der vollständigen Dokumentation.
Eine weitere Möglichkeit, die Sichtbarkeit eines Composeables zu animieren, besteht darin, den Alphawert mit animateFloatAsState
im Zeitverlauf zu animieren:
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) ) { }
Wenn Sie den Alphawert ändern, bleibt das Element jedoch in der Komposition und nimmt weiterhin den ihm zugewiesenen Platz ein. Dies kann dazu führen, dass Screenreader und andere Bedienungshilfen das Element auf dem Bildschirm weiterhin berücksichtigen. Mit AnimatedVisibility
wird das Element dagegen irgendwann aus der Komposition entfernt.
Hintergrundfarbe animieren
val animatedColor by animateColorAsState( if (animateBackgroundColor) colorGreen else colorBlue, label = "color" ) Column( modifier = Modifier.drawBehind { drawRect(animatedColor) } ) { // your composable here }
Diese Option ist leistungsfähiger als die Verwendung von Modifier.background()
.
Modifier.background()
ist für eine einmalige Farbeinstellung akzeptabel, aber wenn eine Farbe im Zeitverlauf animiert wird, kann dies zu mehr Neuzusammensetzungen als nötig führen.
Informationen zum Endlos-Animieren der Hintergrundfarbe finden Sie im Abschnitt Animation wiederholen.
Größe eines Composeables animieren
Mit Compose können Sie die Größe von Composeables auf verschiedene Arten animieren. Verwenden Sie animateContentSize()
für Animationen zwischen Größenänderungen von Kompositionen.
Wenn Sie beispielsweise ein Feld mit Text haben, der sich von einer auf mehrere Zeilen ausdehnen kann, können Sie mit Modifier.animateContentSize()
einen flüssigeren Übergang erzielen:
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 } ) { }
Sie können auch AnimatedContent
mit einem SizeTransform
verwenden, um zu beschreiben, wie Größenänderungen erfolgen sollen.
Position des Composeables animieren
Wenn Sie die Position eines Composeables animieren möchten, verwenden Sie Modifier.offset{ }
in Kombination mit 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 } )
Wenn Sie dafür sorgen möchten, dass Elemente beim Animieren von Position oder Größe nicht über oder unter anderen Elementen gezeichnet werden, verwenden Sie Modifier.layout{ }
. Dieser Modifikator überträgt Größe und Position an das übergeordnete Element, was sich dann auf andere untergeordnete Elemente auswirkt.
Wenn Sie beispielsweise eine Box
innerhalb einer Column
verschieben und die anderen untergeordneten Elemente sich ebenfalls verschieben sollen, fügen Sie die Informationen zum Offset wie unten dargestellt in Modifier.layout{ }
ein:Box
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) ) }
Ränder eines Composeables animieren
Wenn Sie den Abstand eines Composeables animieren möchten, verwenden Sie animateDpAsState
in Kombination mit 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 } )
Höhe eines Composeables animieren
Verwenden Sie animateDpAsState
in Kombination mit Modifier.graphicsLayer{ }
, um die Höhe eines Composeables zu animieren. Verwenden Sie für einmalige Höhenänderungen Modifier.shadow()
. Wenn Sie den Schatten animieren, ist die Verwendung des Modifier.graphicsLayer{ }
-Modifikators die leistungsfähigere Option.
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) ) { }
Alternativ können Sie das Card
-Kompositelement verwenden und die Höhe für jeden Status auf einen anderen Wert festlegen.
Text skalieren, verschieben oder drehen
Wenn Sie Text skalieren, verschieben oder drehen möchten, legen Sie den Parameter textMotion
auf TextStyle
auf TextMotion.Animated
fest. So werden flüssigere Übergänge zwischen Textanimationen ermöglicht. Verwenden Sie Modifier.graphicsLayer{ }
, um den Text zu übersetzen, zu drehen oder zu skalieren.
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) ) }
Schriftfarbe animieren
Verwenden Sie das color
-Lambda für das BasicText
-Komposit, um die Textfarbe zu animieren:
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 }, // ... )
Zwischen verschiedenen Inhaltstypen wechseln
Mit AnimatedContent
kannst du zwischen verschiedenen Composeables animieren. Wenn du nur einen Standard-Überblendungseffekt zwischen Composeables haben möchtest, verwende 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
kann so angepasst werden, dass viele verschiedene Arten von Ein- und Ausblendungsübergängen angezeigt werden. Weitere Informationen finden Sie in der Dokumentation zu AnimatedContent
oder in diesem Blogpost zu
AnimatedContent
.
Animationen beim Wechseln zwischen verschiedenen Zielen
Wenn Sie Übergänge zwischen Komponenten animieren möchten, wenn Sie das Artefakt navigation-compose verwenden, geben Sie enterTransition
und exitTransition
für eine Komponente an. Sie können die Standardanimation auch so festlegen, dass sie für alle Ziele auf der obersten Ebene NavHost
verwendet wird:
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( // ... ) } }
Es gibt viele verschiedene Arten von Ein- und Ausblendungsübergängen, die unterschiedliche Effekte auf die ein- und ausgehenden Inhalte anwenden. Weitere Informationen finden Sie in der Dokumentation.
Animation wiederholen
Verwenden Sie rememberInfiniteTransition
mit einem infiniteRepeatable
animationSpec
, um die Animation fortlaufend zu wiederholen. Ändern Sie RepeatModes
, um anzugeben, wie vor- und zurückgegangen werden soll.
Mit finiteRepeatable
können Sie eine bestimmte Anzahl von Wiederholungen festlegen.
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 }
Animation beim Starten eines Composeables starten
LaunchedEffect
wird ausgeführt, wenn ein Composeable in die Komposition eintritt. Damit wird beim Starten eines Composeables eine Animation gestartet. Sie können damit den Änderungsstatus der Animation steuern. Animatable
mit der Methode animateTo
verwenden, um die Animation beim Starten zu starten:
val alphaAnimation = remember { Animatable(0f) } LaunchedEffect(Unit) { alphaAnimation.animateTo(1f) } Box( modifier = Modifier.graphicsLayer { alpha = alphaAnimation.value } )
Sequenzielle Animationen erstellen
Verwenden Sie die Animatable
-Coroutinen-APIs, um sequenzielle oder parallele Animationen auszuführen. Wenn Sie animateTo
nacheinander auf Animatable
anwenden, wartet jede Animation, bis die vorherigen Animationen abgeschlossen sind, bevor sie fortfährt .
Das liegt daran, dass es sich um eine Pausierfunktion handelt.
val alphaAnimation = remember { Animatable(0f) } val yAnimation = remember { Animatable(0f) } LaunchedEffect("animationKey") { alphaAnimation.animateTo(1f) yAnimation.animateTo(100f) yAnimation.animateTo(500f, animationSpec = tween(100)) }
Parallele Animationen erstellen
Verwenden Sie die Coroutinen-APIs (Animatable#animateTo()
oder animate
) oder die Transition
API, um gleichzeitige Animationen zu erzielen. Wenn Sie mehrere Startfunktionen in einem coroutine-Kontext verwenden, werden die Animationen gleichzeitig gestartet:
val alphaAnimation = remember { Animatable(0f) } val yAnimation = remember { Animatable(0f) } LaunchedEffect("animationKey") { launch { alphaAnimation.animateTo(1f) } launch { yAnimation.animateTo(100f) } }
Mit der updateTransition
API können Sie denselben Status verwenden, um viele verschiedene Unterkunftsanimationen gleichzeitig zu steuern. Im folgenden Beispiel werden zwei Eigenschaften animiert, die durch einen Zustandswechsel gesteuert werden: rect
und 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 } }
Animationsleistung optimieren
Animationen in Compose können zu Leistungsproblemen führen. Das liegt an der Natur einer Animation: Pixel werden auf dem Bildschirm schnell und Frame für Frame bewegt oder geändert, um die Illusion von Bewegung zu erzeugen.
Berücksichtigen Sie die verschiedenen Phasen des Komponierens: Komposition, Layout und Zeichnen. Wenn sich durch Ihre Animation die Layoutphase ändert, müssen alle betroffenen Composeables neu layoutet und neu gezeichnet werden. Wenn die Animation in der Zeichenphase erfolgt, ist sie standardmäßig leistungsfähiger als wenn Sie die Animation in der Layoutphase ausführen würden, da insgesamt weniger Arbeit anfällt.
Damit Ihre App während der Animation so wenig wie möglich belastet wird, wählen Sie nach Möglichkeit die Lambda-Version eines Modifier
aus. Dadurch wird die Neukomposition übersprungen und die Animation wird außerhalb der Kompositionphase ausgeführt. Andernfalls verwenden Sie Modifier.graphicsLayer{ }
, da dieser Modifikator immer in der Zeichenphase ausgeführt wird. Weitere Informationen finden Sie im Abschnitt Lesen verschieben der Leistungsdokumentation.
Timing der Animation ändern
In Compose werden standardmäßig Feder-Animationen für die meisten Animationen verwendet. Federn oder physikbasierte Animationen wirken natürlicher. Sie können auch unterbrochen werden, da sie die aktuelle Geschwindigkeit des Objekts anstelle einer festen Zeit berücksichtigen.
Wenn Sie die Standardeinstellung überschreiben möchten, können Sie mit allen oben genannten Animation APIs einen animationSpec
festlegen, um die Ausführung einer Animation anzupassen. So können Sie beispielsweise festlegen, dass sie über einen bestimmten Zeitraum ausgeführt werden soll oder dass sie federleichter sein soll.
Im Folgenden finden Sie eine Zusammenfassung der verschiedenen animationSpec
-Optionen:
spring
: Physikbasierte Animation, Standardeinstellung für alle Animationen. Sie können die Steifigkeit oder das Dämpfungsverhältnis ändern, um ein anderes Erscheinungsbild der Animation zu erzielen.tween
(Kurzform für between): Dauerbasierte Animation, bei der der Übergang zwischen zwei Werten mit einerEasing
-Funktion animiert wird.keyframes
: Spezifikation zum Angeben von Werten an bestimmten Schlüsselpunkten in einer Animation.repeatable
: Dauerbasierte Spezifikation, die eine bestimmte Anzahl von Malen ausgeführt wird, die durchRepeatMode
angegeben wird.infiniteRepeatable
: Dauerbasierte Spezifikation, die unbegrenzt läuft.snap
: Der Endwert wird sofort und ohne Animation erreicht.
Weitere Informationen zu animationSpecs finden Sie in der vollständigen Dokumentation.
Weitere Informationen
Weitere Beispiele für lustige Animationen in Compose:
- 5 schnelle Animationen in Compose
- Jellyfish in der Zeichenansicht bewegen
AnimatedContent
in der Zeichenansicht anpassen- Einführung in die Übergänge in Compose