Animationsmodifikatoren und zusammensetzbare Funktionen

Compose bietet integrierte Composables 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 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 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.

EnterTransition und ExitTransition Beispiele

Übergang ExitTransition
fadeIn
Animation einblenden
fadeOut
Ausblendungsanimation
slideIn
Einblendungsanimation
slideOut
Animation zum Ausblenden
slideInHorizontally
Horizontal einblenden
slideOutHorizontally
Animation horizontal herausziehen
slideInVertically
vertikal einblendende Animation
slideOutVertically
vertikale Ausfahranimation
scaleIn
Zoom-in-Animation
scaleOut
Animation skalieren
expandIn
in der Animation maximieren
shrinkOut
Animation verkleinern
expandHorizontally
horizontale Ausweitungsanimation
shrinkHorizontally
Animation horizontal verkleinern
expandVertically
vertikale Maximierungsanimation
shrinkVertically
Animation vertikal verkleinern

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. 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 Ausblenden 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 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 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 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 ausgeblendet. Dieses Verhalten wird als Überblenden 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 angezeigt werden 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 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 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. 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 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üne zusammensetzbare Funktion, deren Größe animiert wird, ändert sich reibungslos.
Abbildung 2: Zusammensetzbare Animation zwischen einer kleinen und einer größeren Größe

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
        }

) {
}

Animationen für Listenelemente

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.