Animationsmodifikatoren und zusammensetzbare Funktionen

Compose bietet integrierte Composables und Modifier für gängige Animationsanwendungsfälle.

Integrierte animierte Composables

Ein- und Ausblenden mit AnimatedVisibility animieren

Grüne Komponente, die sich selbst ein- und ausblendet
Abbildung 1: Ein- und Ausblenden eines Elements in einer Spalte animieren

Die Composable AnimatedVisibility animiert das Ein- und Ausblenden ihres Inhalts.

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 durch Einblenden und Vergrößern angezeigt und durch Ausblenden und Verkleinern ausgeblendet. 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 ist, können Sie mehrere EnterTransition- oder ExitTransition-Objekte mit einem +-Operator kombinieren. Für jedes Objekt sind optionale Parameter verfügbar, mit denen Sie das Verhalten anpassen können. Weitere Informationen finden Sie in den Referenzen.

Beispiele für EnterTransition und ExitTransition

EnterTransition ExitTransition
fadeIn
Einblendanimation
fadeOut
Ausblendanimation
slideIn
Einblendanimation
slideOut
Animation zum Ausblenden von Folien
slideInHorizontally
Animation „Horizontal einblenden“
slideOutHorizontally
Animation zum horizontalen Ausblenden von Folien
slideInVertically
Vertikal einblenden
slideOutVertically
Vertikale Ausblendanimation
scaleIn
Animation skalieren
scaleOut
Animation zum Herauszoomen
expandIn
In Animation einblenden
shrinkOut
Animation zum Verkleinern
expandHorizontally
Animation „Horizontal erweitern“
shrinkHorizontally
Animation „Horizontal verkleinern“
expandVertically
Animation „Vertikal erweitern“
shrinkVertically
Animation „Vertikal verkleinern“

AnimatedVisibility bietet auch eine Variante, die einen MutableTransitionState akzeptiert. So können Sie eine Animation auslösen, sobald das AnimatedVisibility dem Kompositionsbaum hinzugefügt wird. Es 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 innerhalb von AnimatedVisibility (direkte oder indirekte untergeordnete Elemente) kann mit dem Modifikator animateEnterExit ein unterschiedliches Animationsverhalten angegeben werden. Der visuelle Effekt für jedes dieser untergeordneten Elemente ist eine Kombination aus den Animationen, die in der AnimatedVisibility-Composable-Funktion angegeben sind, und den eigenen Ein- und Ausblendeanimationen 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 überhaupt keine Animationen anwendet, damit untergeordnete Elemente jeweils eigene Animationen von animateEnterExit haben können. Geben Sie dazu EnterTransition.None und ExitTransition.None für die zusammensetzbare Funktion AnimatedVisibility an.

Benutzerdefinierte Animation hinzufügen

Wenn Sie benutzerdefinierte Animationseffekte hinzufügen möchten, die über die integrierten Ein- und Ausblendeanimationen hinausgehen, greifen Sie über die transition-Eigenschaft in der Content-Lambda für AnimatedVisibility auf die zugrunde liegende Transition-Instanz zu. Alle Animationsstatus, die der Übergang-Instanz hinzugefügt werden, werden gleichzeitig mit den Ein- und Ausblendeanimationen von AnimatedVisibility ausgeführt. AnimatedVisibility wartet, bis alle Animationen in Transition abgeschlossen sind, bevor der Inhalt entfernt wird. Bei Exit-Animationen, die unabhängig von Transition erstellt wurden (z. B. mit animate*AsState), kann AnimatedVisibility sie nicht berücksichtigen und entfernt daher möglicherweise das Content-Composable, bevor die Animation abgeschlossen 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.

Animation basierend auf dem Zielstatus mit AnimatedContent

Die AnimatedContent-Composable-Funktion animiert ihren 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")
    }
}

Beachten Sie, dass Sie immer den Lambda-Parameter verwenden und ihn im Inhalt berücksichtigen sollten. 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 Durchblenden bezeichnet. Sie können dieses Animationsverhalten anpassen, indem Sie dem Parameter transitionSpec ein ContentTransform-Objekt zuweisen. Sie können ContentTransform erstellen, indem Sie ein EnterTransition mit einem ExitTransition mit 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() 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 ausgeblendet werden sollen. Zusätzlich zu allen EnterTransition- und ExitTransition-Funktionen, die für AnimatedVisibility verfügbar sind, bietet AnimatedContent slideIntoContainer und slideOutOfContainer. Sie sind praktische Alternativen zu slideInHorizontally/Vertically und slideOutHorizontally/Vertically, mit denen der Abstand zwischen den Folien anhand der Größen des ursprünglichen Inhalts und des Zielinhalts des AnimatedContent-Inhalts berechnet wird.

Mit SizeTransform wird festgelegt, wie die Größe zwischen den ursprünglichen und den Zielinhalten animiert werden soll. Beim Erstellen der Animation haben Sie Zugriff auf die ursprüngliche und die Zielgröße. Mit SizeTransform wird auch gesteuert, ob der Inhalt während 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)) 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()
        }
    }
}

Ein- und Ausblendung von untergeordneten Elementen animieren

Wie bei AnimatedVisibility ist der Modifier animateEnterExit in der Content-Lambda von AnimatedContent verfügbar. Damit können Sie EnterAnimation und ExitAnimation auf jedes der direkten oder indirekten untergeordneten Elemente anwenden.

Benutzerdefinierte Animation hinzufügen

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

Mit Crossfade zwischen zwei Layouts animieren

Crossfade animiert zwischen zwei Layouts mit einer Überblendung. Durch Umschalten des an den Parameter current übergebenen Werts wird der Inhalt mit einer Überblendung ein- und ausgeblendet.

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

Integrierte Modifikatoren für Animationen

Composable-Größenänderungen mit animateContentSize animieren

Grüne Composable, deren Größe sich fließend ändert.
Abbildung 2. Composable, das reibungslos zwischen einer kleinen und einer größeren Größe animiert wird

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 das Neuanordnen von Elementen in einer Lazy List oder einem Lazy Grid animieren möchten, lesen Sie die Dokumentation zur Animation von Lazy-Layout-Elementen.