Einzelnen Wert mit animate*AsState
animieren
Die animate*AsState
-Funktionen sind die einfachsten Animations-APIs in Compose für
zum Animieren eines einzelnen Werts. Sie geben nur den Zielwert (oder Endwert) an.
Die API startet die Animation vom aktuellen Wert bis zum angegebenen Wert.
Im Folgenden finden Sie ein Beispiel für eine Animation der Alphaversion mit dieser API. Durch das einfache Einbinden des
Zielwert in animateFloatAsState
ist der Alphawert jetzt ein Animationswert
zwischen den angegebenen Werten (in diesem Fall 1f
oder 0.5f
).
var enabled by remember { mutableStateOf(true) } val alpha: Float by animateFloatAsState(if (enabled) 1f else 0.5f) Box( Modifier.fillMaxSize() .graphicsLayer(alpha = alpha) .background(Color.Red) )
Sie müssen keine Instanz einer Animationsklasse oder eines
Unterbrechung. Intern ist ein Animationsobjekt (ein Animatable
)
Instanz) erstellt und gespeichert wird, wobei die erste Ziel-URL
-Wert als Anfangswert festlegen. Sobald Sie diese zusammensetzbare Funktion bereitstellen,
wird automatisch eine Animation gestartet,
Wert. Wenn bereits eine Animation läuft, beginnt die Animation bei
Aktueller Wert (und Geschwindigkeit) und führt eine Animation in Richtung des Zielwerts. Während der
Animation wird diese zusammensetzbare Funktion neu zusammengesetzt und gibt eine aktualisierte Animation zurück
jeden Frame einen Wert hat.
„Compose“ bietet animate*AsState
-Funktionen für Float
,
Color
, Dp
, Size
, Offset
, Rect
, Int
, IntOffset
und
IntSize
Sie können problemlos andere Datentypen unterstützen, indem Sie eine
TwoWayConverter
auf animateValueAsState
, die einen generischen Typ verwenden.
Sie können die Animationsspezifikationen anpassen, indem Sie ein AnimationSpec
angeben.
Weitere Informationen finden Sie unter AnimationSpec.
Mehrere Eigenschaften gleichzeitig mit einem Übergang animieren
Transition
verwaltet eine oder mehrere Animationen als untergeordnete Elemente und führt sie aus
gleichzeitig zwischen mehreren Bundesstaaten.
Die Status können einen beliebigen Datentyp haben. In vielen Fällen können Sie eine benutzerdefinierte enum
um die Typsicherheit zu gewährleisten. Beispiel:
enum class BoxState { Collapsed, Expanded }
updateTransition
erstellt und merkt sich eine Instanz von Transition
und aktualisiert sie
und deren Zustand.
var currentState by remember { mutableStateOf(BoxState.Collapsed) } val transition = updateTransition(currentState, label = "box state")
Sie können dann mit einer der animate*
-Erweiterungsfunktionen ein untergeordnetes Element definieren
an diesem Übergang zu einer Animation ein. Geben Sie die Zielwerte für jeden Bundesstaat an.
Diese animate*
-Funktionen geben einen Animationswert zurück, der jeden Frame aktualisiert wird.
während der Animation, wenn der Übergangsstatus mit
updateTransition
.
val rect by transition.animateRect(label = "rectangle") { state -> when (state) { BoxState.Collapsed -> Rect(0f, 0f, 100f, 100f) BoxState.Expanded -> Rect(100f, 100f, 300f, 300f) } } val borderWidth by transition.animateDp(label = "border width") { state -> when (state) { BoxState.Collapsed -> 1.dp BoxState.Expanded -> 0.dp } }
Optional können Sie einen transitionSpec
-Parameter übergeben, um einen anderen
AnimationSpec
für jede der Kombinationen von Änderungen des Übergangsstatus. Weitere Informationen finden Sie unter
AnimationSpec.
val color by transition.animateColor( transitionSpec = { when { BoxState.Expanded isTransitioningTo BoxState.Collapsed -> spring(stiffness = 50f) else -> tween(durationMillis = 500) } }, label = "color" ) { state -> when (state) { BoxState.Collapsed -> MaterialTheme.colorScheme.primary BoxState.Expanded -> MaterialTheme.colorScheme.background } }
Sobald ein Übergang im Zielstatus erreicht ist, Transition.currentState
ist mit Transition.targetState
identisch. Dies kann als Signal für
ob der Wechsel abgeschlossen ist.
Manchmal möchten wir einen anderen Anfangszustand als das erste Ziel verwenden.
Bundesstaat. Wir können updateTransition
mit MutableTransitionState
verwenden, um
dies. So können wir z. B. die Animation starten, sobald der Code
Zusammensetzung.
// Start in collapsed state and immediately animate to expanded var currentState = remember { MutableTransitionState(BoxState.Collapsed) } currentState.targetState = BoxState.Expanded val transition = updateTransition(currentState, label = "box state") // ……
Für einen komplexeren Übergang mit mehreren zusammensetzbaren Funktionen können Sie
createChildTransition
verwenden
um einen untergeordneten Übergang zu erstellen. Diese Technik ist nützlich,
in einer komplexen zusammensetzbaren Funktion
auf mehrere Unterkomponenten zugreifen. Die übergeordnete Umstellung
alle Animationswerte in den untergeordneten Übergängen beachten.
enum class DialerState { DialerMinimized, NumberPad } @Composable fun DialerButton(isVisibleTransition: Transition<Boolean>) { // `isVisibleTransition` spares the need for the content to know // about other DialerStates. Instead, the content can focus on // animating the state change between visible and not visible. } @Composable fun NumberPad(isVisibleTransition: Transition<Boolean>) { // `isVisibleTransition` spares the need for the content to know // about other DialerStates. Instead, the content can focus on // animating the state change between visible and not visible. } @Composable fun Dialer(dialerState: DialerState) { val transition = updateTransition(dialerState, label = "dialer state") Box { // Creates separate child transitions of Boolean type for NumberPad // and DialerButton for any content animation between visible and // not visible NumberPad( transition.createChildTransition { it == DialerState.NumberPad } ) DialerButton( transition.createChildTransition { it == DialerState.DialerMinimized } ) } }
Übergang mit AnimatedVisibility
und AnimatedContent
verwenden
AnimatedVisibility
und AnimatedContent
sind als Erweiterungsfunktionen von Transition
verfügbar. Das targetState
für
Transition.AnimatedVisibility
und Transition.AnimatedContent
werden abgeleitet
aus Transition
und löst je nach Bedarf einen Wechsel zum Ein-/Ausstieg aus, wenn der
targetState
von Transition
hat sich geändert. Mit diesen Erweiterungsfunktionen
die Enter-/Exit-/sizeTransform-Animationen, die andernfalls intern wären.
AnimatedVisibility
/AnimatedContent
werden in die Transition
geschoben.
Mit diesen Erweiterungsfunktionen wird der Status von AnimatedVisibility
/AnimatedContent
Veränderung von außen beobachtet werden kann. Anstelle eines booleschen visible
-Parameters
Diese Version von AnimatedVisibility
verwendet eine Lambda-Funktion, die das übergeordnete Element
Zielstatus des Übergangs in einen booleschen Wert.
Weitere Informationen finden Sie unter animateVisibility und animateContent.
var selected by remember { mutableStateOf(false) } // Animates changes when `selected` is changed. val transition = updateTransition(selected, label = "selected state") val borderColor by transition.animateColor(label = "border color") { isSelected -> if (isSelected) Color.Magenta else Color.White } val elevation by transition.animateDp(label = "elevation") { isSelected -> if (isSelected) 10.dp else 2.dp } Surface( onClick = { selected = !selected }, shape = RoundedCornerShape(8.dp), border = BorderStroke(2.dp, borderColor), elevation = elevation ) { Column(modifier = Modifier.fillMaxWidth().padding(16.dp)) { Text(text = "Hello, world!") // AnimatedVisibility as a part of the transition. transition.AnimatedVisibility( visible = { targetSelected -> targetSelected }, enter = expandVertically(), exit = shrinkVertically() ) { Text(text = "It is fine today.") } // AnimatedContent as a part of the transition. transition.AnimatedContent { targetState -> if (targetState) { Text(text = "Selected") } else { Icon(imageVector = Icons.Default.Phone, contentDescription = "Phone") } } } }
Übergang kapseln und wiederverwendbar machen
Für einfache Anwendungsfälle lassen sich Übergangsanimationen in derselben zusammensetzbaren Funktion wie ist dies eine gute Option. Wenn Sie an einer komplexen Komponente arbeiten mit einer Reihe animierter Werte zu verknüpfen. Sie sollten die die Animationsimplementierung über die zusammensetzbare Benutzeroberfläche.
Erstellen Sie dazu eine Klasse, die alle Animationswerte und ein eine "update"-Funktion, die eine Instanz dieser Klasse zurückgibt. Der Übergang Implementierung in die neue separate Funktion extrahiert. Dieses Muster ist nützlich, wenn Sie die Animationslogik zentralisieren oder komplexe Animationen wiederverwendbar.
enum class BoxState { Collapsed, Expanded } @Composable fun AnimatingBox(boxState: BoxState) { val transitionData = updateTransitionData(boxState) // UI tree Box( modifier = Modifier .background(transitionData.color) .size(transitionData.size) ) } // Holds the animation values. private class TransitionData( color: State<Color>, size: State<Dp> ) { val color by color val size by size } // Create a Transition and return its animation values. @Composable private fun updateTransitionData(boxState: BoxState): TransitionData { val transition = updateTransition(boxState, label = "box state") val color = transition.animateColor(label = "color") { state -> when (state) { BoxState.Collapsed -> Color.Gray BoxState.Expanded -> Color.Red } } val size = transition.animateDp(label = "size") { state -> when (state) { BoxState.Collapsed -> 64.dp BoxState.Expanded -> 128.dp } } return remember(transition) { TransitionData(color, size) } }
Mit rememberInfiniteTransition
eine sich endlos wiederholende Animation erstellen
InfiniteTransition
enthält eine oder mehrere untergeordnete Animationen wie Transition
, aber
werden die Animationen gestartet, sobald die Komposition beginnt.
solange sie nicht entfernt werden. Sie können eine Instanz von InfiniteTransition
erstellen
mit rememberInfiniteTransition
. Untergeordnete Animationen können hinzugefügt werden mit
animateColor
, animatedFloat
oder animatedValue
. Außerdem müssen Sie eine
unfiniteRepeatable zum Angeben der Animation.
Spezifikationen.
val infiniteTransition = rememberInfiniteTransition() val color by infiniteTransition.animateColor( initialValue = Color.Red, targetValue = Color.Green, animationSpec = infiniteRepeatable( animation = tween(1000, easing = LinearEasing), repeatMode = RepeatMode.Reverse ) ) Box(Modifier.fillMaxSize().background(color))
Low-Level-Animations-APIs
Alle im vorherigen Abschnitt erwähnten High-Level-Animations-APIs basieren auf der Low-Level-Animations-APIs.
Die animate*AsState
-Funktionen sind die einfachsten APIs, die eine Instant-
als Animationswert verwendet werden. Es wird durch Animatable
unterstützt, was eine
koroutinebasierte API zur Animation eines einzelnen Werts. updateTransition
erstellt einen
Übergangsobjekt, das mehrere Animationswerte verwalten und basierend auf diesen
zu einer Zustandsänderung. rememberInfiniteTransition
ist ähnlich, erstellt aber ein
Endloser Übergang, mit dem mehrere Animationen verwaltet werden können, die weiter ausgeführt werden
auf unbestimmte Zeit. Alle diese APIs sind zusammensetzbar, mit Ausnahme von Animatable
, das
Diese Animationen können außerhalb der Komposition erstellt werden.
Alle diese APIs basieren auf der grundlegenden Animation
API. Die meisten
Apps interagieren nicht direkt mit Animation
, einige der Anpassungen
Funktionen für Animation
sind über übergeordnete APIs verfügbar. Weitere Informationen finden Sie unter
Passen Sie Animationen an, um mehr über die
AnimationVector
und AnimationSpec
.
Animatable
: Koroutinebasierte Einzelwertanimation
Animatable
ist ein Werthalter, der den Wert animieren kann, sobald er über
animateTo
. Dies ist die API, mit der die Implementierung von animate*AsState
gesichert wird.
Es sorgt für eine einheitliche Fortsetzung und gegenseitige Exklusivität, d. h., das
Die Wertänderung erfolgt immer kontinuierlich und alle laufenden Animationen werden abgebrochen.
Viele Funktionen von Animatable
, einschließlich animateTo
, werden als gesperrt angeboten
Funktionen. Das bedeutet, dass sie in eine geeignete Koroutine eingebunden werden müssen.
Umfang. Sie können beispielsweise die zusammensetzbare Funktion LaunchedEffect
verwenden, um ein
für die Dauer des
angegebenen Schlüssel/Wert-Paars festgelegt.
// Start out gray and animate to green/red based on `ok` val color = remember { Animatable(Color.Gray) } LaunchedEffect(ok) { color.animateTo(if (ok) Color.Green else Color.Red) } Box(Modifier.fillMaxSize().background(color.value))
Im obigen Beispiel wird eine Instanz von Animatable
mit
den Anfangswert von Color.Gray
. Je nach Wert des booleschen Flags
ok
wird die Farbe zu Color.Green
oder Color.Red
animiert. Alle nachfolgenden
Durch Ändern in den booleschen Wert wird die Animation in die andere Farbe gestartet. Wenn es eine
wenn der Wert geändert wird, die Animation abgebrochen wird und der
Neue Animation beginnt beim aktuellen Snapshot-Wert mit der aktuellen Geschwindigkeit.
Dies ist die Animationsimplementierung, die die animate*AsState
API sichert
die wir im vorherigen Abschnitt erwähnt haben. Im Vergleich zu animate*AsState
bei Verwendung von
Mit Animatable
können wir mehrere Aspekte direkt präziser steuern. Erstens:
Animatable
kann einen Anfangswert haben, der sich vom ersten Zielwert unterscheidet.
Das obige Codebeispiel zeigt zunächst ein graues Feld, das sofort
zu Grün oder Rot. Zweitens bietet Animatable
mehr
Vorgänge für den Inhaltswert, nämlich snapTo
und animateDecay
. snapTo
setzt den aktuellen Wert sofort auf den Zielwert. Dies ist nützlich, wenn der
Animationen selbst sind nicht die einzige Informationsquelle und müssen mit anderen
z. B. Touch-Events. animateDecay
startet eine Animation, die langsamer wird
von der gegebenen Geschwindigkeit ab. Dies ist nützlich, um das Fling-Verhalten zu implementieren. Weitere Informationen finden Sie unter
Weitere Informationen finden Sie unter Geste und Animation.
Standardmäßig unterstützt Animatable
Float
und Color
, aber jeder Datentyp kann
durch Angabe einer TwoWayConverter
verwendet werden. Weitere Informationen finden Sie unter
AnimationVector für weitere Informationen ein.
Sie können die Animationsspezifikationen durch Angabe eines AnimationSpec
anpassen.
Weitere Informationen finden Sie unter AnimationSpec.
Animation
: Manuell gesteuerte Animation
Animation
ist die Animations-API auf der niedrigsten Ebene. Viele der Animationen
die wir bisher gesehen haben,
auf der Animation aufbauen. Es gibt zwei Animation
-Untertypen:
TargetBasedAnimation
und DecayAnimation
.
Animation
sollte nur verwendet werden, um die Zeit der Animation manuell zu steuern.
Animation
ist zustandslos und hat kein Lebenszykluskonzept. Es
dient als Berechnungs-Modul für Animationen, das von den übergeordneten APIs verwendet wird.
TargetBasedAnimation
Andere APIs decken die meisten Anwendungsfälle ab, aber die direkte Verwendung von TargetBasedAnimation
können Sie die Wiedergabedauer der Animation selbst steuern. Im Beispiel unten
Die Wiedergabedauer von TargetAnimation
wird manuell basierend auf dem Frame gesteuert.
Uhrzeit von withFrameNanos
angegeben.
val anim = remember { TargetBasedAnimation( animationSpec = tween(200), typeConverter = Float.VectorConverter, initialValue = 200f, targetValue = 1000f ) } var playTime by remember { mutableStateOf(0L) } LaunchedEffect(anim) { val startTime = withFrameNanos { it } do { playTime = withFrameNanos { it } - startTime val animationValue = anim.getValueFromNanos(playTime) } while (someCustomCondition()) }
DecayAnimation
Im Gegensatz zu TargetBasedAnimation
,
DecayAnimation
erfordert keine Angabe einer targetValue
. Stattdessen berechnet es seine
targetValue
basierend auf den von initialVelocity
festgelegten Startbedingungen und
initialValue
und die bereitgestellte DecayAnimationSpec
.
Decay-Animationen werden oft nach einem Flachgeste verwendet, um Elemente
anhalten. Die Animationsgeschwindigkeit beginnt bei dem von initialVelocityVector
festgelegten Wert
und wird mit der Zeit langsamer.
Empfehlungen für dich
- Hinweis: Der Linktext wird angezeigt, wenn JavaScript deaktiviert ist.
- Animationen anpassen {:#customize-animations}
- Animationen in „Compose“
- Modifikatoren für Animationen und zusammensetzbare Funktionen