Animationsmodifikatoren und zusammensetzbare Funktionen

Compose bietet integrierte Composeables und Modifikatoren für gängige Anwendungsfälle von Animationen.

Eingebaute animierte Composeables

Mit AnimatedVisibility das Erscheinen und Verschwinden animieren

Grüner zusammensetzbarer Inhalt, der sich selbst ein- und ausblendet
Abbildung 1: Das Erscheinen und Verschwinden eines Elements in einer Spalte animieren

Mit dem AnimatedVisibility-Kompositelement wird das Ein- und Ausblenden des 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
    // ...
}

Standardmäßig werden die Inhalte eingeblendet, indem sie eingeblendet und maximiert werden, und ausgeblendet, indem sie ausgeblendet und minimiert werden. Der Übergang kann angepasst werden, indem EnterTransition und ExitTransition angegeben 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 Beispiel oben zu sehen, können Sie mehrere EnterTransition- oder ExitTransition-Objekte mit einem +-Operator kombinieren. Für jeden Operator können optionale Parameter verwendet werden, um das Verhalten anzupassen. Weitere Informationen finden Sie in den Referenzen.

Beispiele für EnterTransition und ExitTransition

EnterTransition ExitTransition
fadeIn
Einblendungsanimation
fadeOut
Ausblendungsanimation
slideIn
Einblendungsanimation
slideOut
Ausblendungsanimation
slideInHorizontally
Horizontal einblenden
slideOutHorizontally
Horizontale Ausfahranimation
slideInVertically
vertikal einblendende Animation
slideOutVertically
vertikale Ausfahranimation
scaleIn
Zoom-in-Animation
scaleOut
Animation skalieren
expandIn
in der Animation maximieren
shrinkOut
Schrumpfanimation
expandHorizontally
horizontale Ausweitungsanimation
shrinkHorizontally
Horizontale Schrumpfanimation
expandVertically
vertikale Maximierungsanimation
shrinkVertically
Vertikale Verkleinerungsanimation

AnimatedVisibility bietet auch eine Variante, die einen MutableTransitionState annimmt. So können Sie eine Animation auslösen, sobald die AnimatedVisibility dem Kompositionbaum hinzugefügt wird. Es ist auch nützlich, 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 Ausblenden für Kinder animieren

Für Inhalte innerhalb von AnimatedVisibility (direkte oder indirekte untergeordnete Elemente) kann der Modifikator animateEnterExit verwendet werden, um für jedes Element ein anderes Animationsverhalten festzulegen. Der visuelle Effekt für jedes dieser untergeordneten Elemente ist eine Kombination aus den im AnimatedVisibility-Composit angegebenen Animationen und den eigenen Ein- und Ausblendungsanimationen 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 möchten Sie möglicherweise, dass AnimatedVisibility keine Animationen anwendet, damit Kinder jeweils eigene Animationen von animateEnterExit erhalten. Geben Sie dazu EnterTransition.None und ExitTransition.None für die AnimatedVisibility-Komponente an.

Benutzerdefinierte Animation hinzufügen

Wenn du über die integrierten Ein- und Ausblendungsanimationen hinaus benutzerdefinierte Animationen hinzufügen möchtest, greife über das Attribut transition im Inhalts-Lambda für AnimatedVisibility auf die zugrunde liegende Transition-Instanz zu. Alle der Transition-Instanz hinzugefügten Animationszustände werden gleichzeitig mit den Ein- und Ausblendungsanimationen von AnimatedVisibility ausgeführt. AnimatedVisibility wartet, bis alle Animationen im Transition abgeschlossen sind, bevor der Inhalt entfernt wird. Bei unabhängig von Transition erstellten Beendigungsanimationen (z. B. mit animate*AsState) kann AnimatedVisibility diese nicht berücksichtigen und entfernt die zusammengesetzten Inhalte möglicherweise, 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

Das AnimatedContent-Kompositelement animiert seinen Inhalt, wenn er sich basierend auf einem Zielstatus ändert.

Row {
    var count by remember { mutableIntStateOf(0) }
    Button(onClick = { count++ }) {
        Text("Add")
    }
    AnimatedContent(
        targetState = count,
        label = "animated content"
    ) { targetCount ->
        // Make sure to use `targetCount`, not `count`.
        Text(text = "Count: $targetCount")
    }
}

Sie sollten den Lambda-Parameter immer verwenden und in den Inhalt einbinden. 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 eingeblendet. Dieses Verhalten wird als Wechsel per Überblendung bezeichnet. Sie können dieses Animationsverhalten anpassen, indem Sie dem Parameter transitionSpec ein ContentTransform-Objekt angeben. Sie können ContentTransform erstellen, indem Sie mithilfe der Infixfunktion with einen EnterTransition mit einem ExitTransition kombinieren. Sie können SizeTransform auf den ContentTransform anwenden, indem Sie ihn mit der Infix-Funktion using verknüpfen.

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() togetherWith
                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() togetherWith
                slideOutVertically { height -> height } + fadeOut()
        }.using(
            // Disable clipping since the faded slide-in/out should
            // be displayed out of bounds.
            SizeTransform(clip = false)
        )
    }, label = "animated content"
) { targetCount ->
    Text(text = "$targetCount")
}

Mit EnterTransition wird festgelegt, wie die Zielinhalte erscheinen sollen, und mit ExitTransition, wie die ursprünglichen Inhalte verschwinden sollen. 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, bei denen der Folienabstand basierend auf der Größe des ursprünglichen Inhalts und des Zielinhalts des AnimatedContent-Inhalts berechnet wird.

Mit SizeTransform wird festgelegt, wie die Größe zwischen dem ursprünglichen und dem Zielinhalt animiert werden soll. Beim Erstellen der Animation haben Sie Zugriff auf die Anfangs- und die Zielgröße. Mit SizeTransform wird auch festgelegt, ob die Inhalte während der Animationen auf die Größe der Komponente zugeschnitten werden sollen.

var expanded by remember { mutableStateOf(false) }
Surface(
    color = MaterialTheme.colorScheme.primary,
    onClick = { expanded = !expanded }
) {
    AnimatedContent(
        targetState = expanded,
        transitionSpec = {
            fadeIn(animationSpec = tween(150, 150)) togetherWith
                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
                        }
                    }
                }
        }, label = "size transform"
    ) { targetExpanded ->
        if (targetExpanded) {
            Expanded()
        } else {
            ContentIcon()
        }
    }
}

Übergänge für das Ein- und Ausblenden von Kindern animieren

Wie bei AnimatedVisibility ist der Modifikator animateEnterExit im Inhalts-Lambda von AnimatedContent verfügbar. So können Sie EnterAnimation und ExitAnimation auf jedes direkte oder indirekte untergeordnete Element einzeln anwenden.

Benutzerdefinierte Animation hinzufügen

Wie AnimatedVisibility ist auch das Feld transition im Inhaltslambda von AnimatedContent verfügbar. Damit 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 Crossfade-Animation. Durch Umschalten des an den current-Parameter übergebenen Werts wird der Inhalt mit einer Crossfade-Animation gewechselt.

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

Integrierte Animationsmodifikatoren

Größe von Elementen mit animateContentSize animieren

Grüner zusammensetzbarer Bereich, der seine Größe reibungslos animiert.
Abbildung 2: Kompositionen, die flüssig zwischen einer kleinen und einer größeren Größe animiert werden

Mit dem Modifikator animateContentSize wird eine Größenänderung animiert.

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
        }

) {
}

Listenelement-Animationen

Wenn Sie die Neuanordnung von Elementen in einer Lazy-Liste oder einem Lazy-Raster animieren möchten, lesen Sie die Dokumentation zur Lazy-Layout-Elementanimation.