Animationsmodifikatoren und zusammensetzbare Funktionen

Compose enthält integrierte zusammensetzbare Funktionen und Modifikatoren für häufige Animationsanwendungsfälle.

Integrierte animierte zusammensetzbare Funktionen

Erscheinen und Verschwinden mit AnimatedVisibility animieren

Grüne zusammensetzbare Funktion, die sich selbst ein- und ausgeblendet wird
Abbildung 1: Darstellung und Verschwinden eines Elements in einer Spalte animieren

Die zusammensetzbare Funktion AnimatedVisibility animiert das Erscheinen und Verschwinden ihrer Inhalte.

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
    // ...
}

Standardmäßig wird der Inhalt eingeblendet, indem er ein- und maximiert wird, und wieder ausgeblendet, indem er aus- und verkleinert wird. Die Umstellung kann durch Angabe von EnterTransition und ExitTransition angepasst werden.

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))
}

Wie im obigen Beispiel gezeigt, können mehrere EnterTransition- oder ExitTransition-Objekte mit einem +-Operator kombiniert werden. Jedes Objekt akzeptiert optionale Parameter, um sein Verhalten anzupassen. Weitere Informationen finden Sie in den Referenzen.

Beispiele für EnterTransition und ExitTransition

Übergang starten ExitTransition
fadeIn
Animation einblenden
fadeOut
Ausblendungsanimation
slideIn
Einblendbare Animation
slideOut
Slide-out-Animation
slideInHorizontally
Horizontales Einblenden (Animation)
slideOutHorizontally
Horizontales Herausschieben (Animation)
slideInVertically
Vertikal einblendende Animation
slideOutVertically
Vertikal herausziehbare Animation
scaleIn
Animation skalieren
scaleOut
Animation: Hochskalieren
expandIn
In Animation maximieren
shrinkOut
Animation verkleinern
expandHorizontally
Animation horizontal maximieren
shrinkHorizontally
Animation „Horizontal verkleinern“
expandVertically
Animation vertikal maximieren
shrinkVertically
Animation „Vertikal verkleinern“

AnimatedVisibility bietet auch eine Variante an, für die ein MutableTransitionState-Objekt verwendet wird. So können Sie eine Animation auslösen, sobald AnimatedVisibility dem Zusammensetzungsbaum hinzugefügt wurde. Sie ist auch hilfreich, um den Animationsstatus zu beobachten.

// 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"
        }
    )
}

Ein- und Ausgang für Kinder animieren

Für Inhalte in AnimatedVisibility (direkte oder indirekte untergeordnete Elemente) kann mit dem Modifikator animateEnterExit jeweils ein anderes Animationsverhalten festgelegt werden. Der visuelle Effekt für jedes dieser untergeordneten Elemente ist eine Kombination aus den in der zusammensetzbaren Funktion AnimatedVisibility angegebenen Animationen und den eigenen Einstiegs- und Exit-Animationen des Kindes.

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…
        }
    }
}

In einigen Fällen kann es sinnvoll sein, dass AnimatedVisibility überhaupt keine Animationen anwendet, sodass jedes Kind über animateEnterExit eigene Animationen erstellen kann. Geben Sie dazu bei der zusammensetzbaren Funktion AnimatedVisibility EnterTransition.None und ExitTransition.None an.

Benutzerdefinierte Animation hinzufügen

Wenn Sie neben den integrierten Eingabe- und Exit-Animationen benutzerdefinierte Animationseffekte hinzufügen möchten, greifen Sie über das Attribut transition in der Inhalts-Lambda-Funktion für AnimatedVisibility auf die zugrunde liegende Transition-Instanz zu. Alle Animationsstatus, die der Übergangsinstanz hinzugefügt werden, werden gleichzeitig mit den Eingabe- und Exit-Animationen von AnimatedVisibility ausgeführt. AnimatedVisibility wartet, bis alle Animationen im Transition-Element beendet sind, bevor sein Inhalt entfernt wird. Bei Exit-Animationen, die unabhängig von Transition erstellt wurden (z. B. mit animate*AsState), kann AnimatedVisibility diese nicht berücksichtigen und entfernt daher möglicherweise die zusammensetzbare Funktion, bevor sie fertig ist.

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))
}

Weitere Informationen zu Transition finden Sie unter updateTransition.

Mit AnimatedContent basierend auf dem Zielstatus animieren

Der Inhalt der zusammensetzbaren Funktion AnimatedContent animiert sich, wenn er sich in Abhängigkeit vom Zielstatus ändert.

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")
    }
}

Sie sollten immer den Lambda-Parameter verwenden und im Inhalt widerspiegeln. Die API verwendet diesen Wert als Schlüssel, um den aktuell angezeigten Inhalt zu identifizieren.

Standardmäßig wird der ursprüngliche Inhalt ausgeblendet und dann der Zielinhalt eingeblendet. Dieses Verhalten wird als Überblenden bezeichnet. Sie können dieses Animationsverhalten anpassen, indem Sie ein ContentTransform-Objekt für den transitionSpec-Parameter angeben. Sie können ContentTransform erstellen, indem Sie EnterTransition mit einem ExitTransition mithilfe der Infix-Funktion with kombinieren. Sie können SizeTransform auf ContentTransform anwenden, indem Sie es mit der Infix-Funktion using anhängen.

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 definiert, wie der Zielinhalt erscheinen soll, und ExitTransition definiert, wie der ursprüngliche Inhalt ausgeblendet werden soll. Zusätzlich zu allen für AnimatedVisibility verfügbaren Funktionen EnterTransition und ExitTransition bietet AnimatedContent slideIntoContainer und slideOutOfContainer. Dies sind praktische Alternativen zu slideInHorizontally/Vertically und slideOutHorizontally/Vertically, bei denen der Abstand zwischen den Folien anhand der Größe des Anfangsinhalts und des Zielinhalts des AnimatedContent-Inhalts berechnet wird.

Mit SizeTransform wird definiert, wie die Größe zwischen dem ursprünglichen und dem Zielinhalt animiert wird. Beim Erstellen der Animation können Sie sowohl auf die Anfangs- als auch auf die Zielgröße zugreifen. Mit SizeTransform wird auch festgelegt, ob der Inhalt bei Animationen auf die Komponentengröße zugeschnitten werden soll.

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()
        }
    }
}

Animieren untergeordneter Ein- und Ausstiegsübergänge

Genau wie AnimatedVisibility ist der animateEnterExit-Modifikator in der Inhalts-Lambda-Funktion von AnimatedContent verfügbar. Damit können Sie EnterAnimation und ExitAnimation separat auf die direkten oder indirekten untergeordneten Elemente anwenden.

Benutzerdefinierte Animation hinzufügen

Genau wie AnimatedVisibility ist das Feld transition innerhalb der Inhalts-Lambda-Funktion von AnimatedContent verfügbar. Damit können Sie einen benutzerdefinierten Animationseffekt erstellen, der zeitgleich mit dem AnimatedContent-Übergang ausgeführt wird. Weitere Informationen finden Sie unter updateTransition.

Mit Crossfade zwischen zwei Layouts animieren

Crossfade wird mit einer Überblendungsanimation zwischen zwei Layouts animiert. Durch Umschalten des Werts, der an den current-Parameter übergeben wird, wird der Inhalt mit einer Überblendungsanimation ausgetauscht.

var currentPage by remember { mutableStateOf("A") }
Crossfade(targetState = currentPage) { screen ->
    when (screen) {
        "A" -> Text("Page A")
        "B" -> Text("Page B")
    }
}

Integrierte Animationsmodifikatoren

Größenänderungen bei zusammensetzbaren Funktionen mit animateContentSize animieren

Grünes zusammensetzbares Asset, dessen Größe sich nahtlos ändert.
Abbildung 2: Reibungslose Animation zwischen einer kleinen und einer großen Bildgröße

Der animateContentSize-Modifikator animiert eine Größenänderung.

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
        }

) {
}

Animationen von Listeneinträgen

Wenn Sie die Neuanordnung von Elementen in einer Lazy-Liste oder einem Raster animieren möchten, sehen Sie sich die Dokumentation zur Animation von Lazy-Layout-Elementen an.