Animationsmodifikatoren und zusammensetzbare Funktionen

Compose verfügt über integrierte zusammensetzbare Funktionen und Modifikatoren für gängige Anwendungsfälle in der Animation.

Integrierte animierte zusammensetzbare Funktionen

Aussehen und Verschwinden mit AnimatedVisibility animieren

Grüne zusammensetzbare Funktion, die sich ein- und ausblenden lässt
Abbildung 1. Darstellung und Verschwinden eines Elements in einer Spalte animieren

In der zusammensetzbaren Funktion AnimatedVisibility werden die Darstellung und das Verschwinden ihres Inhalts animiert.

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

Der Inhalt wird standardmäßig eingeblendet und eingeblendet, wenn er eingeblendet wird, und wieder ausgeblendet, wenn er ausgeblendet wird. Der Übergang 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 Sie im obigen Beispiel sehen können, können Sie mehrere EnterTransition- oder ExitTransition-Objekte mit einem +-Operator kombinieren. Jedes Objekt akzeptiert optionale Parameter, um sein Verhalten anzupassen. Weitere Informationen finden Sie in den Referenzen.

Beispiele für EnterTransition und ExitTransition

Übergang Übergang
fadeIn
Animation einblenden
fadeOut
Ausblenden (Animation)
slideIn
Einblendungsanimation
slideOut
Animation zum Ausblenden
slideInHorizontally
Animation horizontal einblenden
slideOutHorizontally
Animation horizontal herausziehen
slideInVertically
Animation senkrecht hineinschieben
slideOutVertically
Animation senkrecht herausschieben
scaleIn
Animation vergrößern
scaleOut
Animation mit horizontaler Skalierung
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 mit MutableTransitionState an. So kannst du eine Animation auslösen, sobald AnimatedVisibility dem Kompositionsbaum hinzugefügt wird. Sie ist auch nützlich, 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 für jeden ein anderes Animationsverhalten angegeben werden. Der visuelle Effekt für jedes dieser untergeordneten Elemente ist eine Kombination aus den Animationen, die in der zusammensetzbaren AnimatedVisibility-Funktion angegeben sind, und den eigenen Eingabe- und Beenden-Animationen des untergeordneten Elements.

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 untergeordnete Elemente jeweils eigene Animationen von animateEnterExit haben können. Dazu geben Sie EnterTransition.None und ExitTransition.None bei der zusammensetzbaren Funktion AnimatedVisibility an.

Benutzerdefinierte Animation hinzufügen

Wenn Sie über die integrierten Animationen zum Ein- und Beenden hinaus benutzerdefinierte Animationseffekte hinzufügen möchten, greifen Sie über das Attribut transition innerhalb der Inhalts-Lambda 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 beendet sind, bevor der 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 daher möglicherweise die zusammensetzbaren Inhalte entfernen, bevor sie fertig sind.

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

Die zusammensetzbare Funktion AnimatedContent animiert den Inhalt, wenn er sich je nach 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")
    }
}

Beachte, dass du immer den Lambda-Parameter verwenden solltest und ihn im Inhalt widerspiegelt. Die API verwendet diesen Wert als Schlüssel, um die aktuell angezeigten Inhalte zu identifizieren.

Standardmäßig wird der ursprüngliche Inhalt ausgeblendet und dann der Zielinhalt ausgeblendet. Dieses Verhalten wird als Überblenden bezeichnet. Sie können dieses Animationsverhalten anpassen, indem Sie ein ContentTransform-Objekt für den Parameter transitionSpec angeben. Sie können ContentTransform erstellen, indem Sie mithilfe der Infix-Funktion with ein EnterTransition mit einem ExitTransition 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")
}

Mit EnterTransition wird definiert, wie der Zielinhalt angezeigt werden soll, und mit ExitTransition, wie der ursprüngliche Inhalt ausgeblendet werden soll. Zusätzlich zu allen EnterTransition- und ExitTransition-Funktionen, die für AnimatedVisibility verfügbar sind, bietet AnimatedContent slideIntoContainer und slideOutOfContainer. Dies sind praktische Alternativen zu slideInHorizontally/Vertically und slideOutHorizontally/Vertically. Sie berechnen die Entfernung für die Folie anhand der Größe des ursprünglichen Inhalts und des Zielinhalts des AnimatedContent-Inhalts.

Mit SizeTransform wird definiert, wie die Größe zwischen dem Anfangs- und dem Zielinhalt animiert werden soll. Beim Erstellen der Animation haben Sie Zugriff auf die Anfangs- und die Zielgröße. Mit SizeTransform wird auch gesteuert, 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()
        }
    }
}

Ein- und Ausblenden von Kindern animieren

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

Benutzerdefinierte Animation hinzufügen

Genau wie bei AnimatedVisibility ist das Feld transition im Lambda von AnimatedContent verfügbar. Hiermit können Sie einen benutzerdefinierten Animationseffekt erstellen, der gleichzeitig mit dem Übergang AnimatedContent ausgeführt wird. Weitere Informationen finden Sie unter updateTransition.

Mit Crossfade zwischen zwei Layouts animieren

Crossfade animiert zwischen zwei Layouts mit einer Überblendungsanimation. Durch Umstellung des an den Parameter current übergebenen Werts wird der Inhalt mit einer Überblendungsanimation umgeschaltet.

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

Integrierte Animationsmodifikatoren

Zusammensetzbare Größenänderungen mit animateContentSize animieren

Grüne zusammensetzbare Funktion, deren Größe animiert wird, ändert sich reibungslos.
Abbildung 2: Zusammensetzbare Animation zwischen einer kleinen und einer größeren Größ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 zu Listenelementen

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