Modyfikatory animacji i elementy kompozycyjne

Funkcja tworzenia ma wbudowane funkcje kompozycyjne i modyfikatory, które pozwalają obsługiwać typowe przypadki użycia animacji.

Wbudowane animowane komponenty

Dodawanie animacji do efektu pojawiania się i znikania za pomocą AnimatedVisibility

Zielony element składany wyświetlający się i ukrywający
Rysunek 1. Animacja pojawiania się i znikania elementu w kolumnie

Komponent AnimatedVisibility animuje pojawianie się i znikanie treści.

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

Domyślnie treść pojawia się, rozjaśniając i rozwijając, oraz znika w wyniku zanikania i pomniejszania. Przejście można dostosować, podając wartości EnterTransition i ExitTransition.

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

Jak widać w przykładzie powyżej, możesz łączyć wiele obiektów EnterTransition lub ExitTransition za pomocą operatora +. Każdy z nich akceptuje opcjonalne parametry, które umożliwiają dostosowanie jego działania. Więcej informacji znajdziesz w dokumentacji.

Przykłady dotyczące EnterTransition i ExitTransition

AnimatedVisibility oferuje też wariant, który przyjmuje parametr MutableTransitionState. Dzięki temu możesz uruchomić animację od razu po dodaniu elementu AnimatedVisibility do drzewa kompozycji. Jest ona też przydatna do obserwowania stanu animacji.

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

Animacja rozpoczęcia i zakończenia dla dzieci

Treści w elemencie podrzędnym AnimatedVisibility (bezpośrednie lub pośrednie) mogą używać modyfikatora animateEnterExit, aby określić inny sposób działania animacji w przypadku każdego z nich. Efekt wizualny dla każdego z tych elementów podrzędnych jest kombinacją animacji określonych w komponowalnym elemencie AnimatedVisibility oraz animacji wejścia i wyjścia elementu podrzędnego.

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

W niektórych przypadkach możesz zdecydować, że AnimatedVisibility nie będzie w ogóle stosować żadnych animacji, żeby każde z dzieci mogły utworzyć własne animacje po animateEnterExit. Aby to zrobić, w komponowalnym elemencie AnimatedVisibility podaj wartości EnterTransition.NoneExitTransition.None.

Dodawanie animacji niestandardowej

Jeśli chcesz dodać niestandardowe efekty animacji oprócz wbudowanych animacji wejścia i wyjścia, uzyskaj dostęp do instancji Transition za pomocą właściwości transition w ramach treści lambda dla AnimatedVisibility. Wszystkie stany animacji dodane do instancji przejścia będą uruchamiane jednocześnie z animacjami wejścia i wyjścia obiektu AnimatedVisibility. AnimatedVisibility czeka, aż wszystkie animacje w Transition się zakończą, a dopiero potem usuwa zawartość. W przypadku animacji wyjścia utworzonych niezależnie od Transition (np. za pomocą animate*AsState) AnimatedVisibility nie będzie w stanie ich uwzględnić, dlatego może usunąć komponent treści przed ich zakończeniem.

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

Więcej informacji o Transition znajdziesz w sekcji updatePrzenoszenie.

Animacja na podstawie stanu docelowego za pomocą AnimatedContent

Komponent AnimatedContent animuje zawartość, która zmienia się w zależności od stanu docelowego.

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

Pamiętaj, że zawsze należy używać parametru lambda i odzwierciedlać go w treści. Interfejs API używa tej wartości jako klucza do identyfikowania treści, która jest aktualnie wyświetlana.

Domyślnie początkowa treść znika, a następnie pojawia się treść docelowa (to zachowanie nazywa się fade through). Możesz dostosować to działanie animacji, określając obiekt ContentTransform w parametrze transitionSpec. Możesz utworzyć ContentTransform, łącząc EnterTransition z ExitTransition za pomocą funkcji infiksu with. Możesz zastosować SizeTransform do ContentTransform, dołączając ją za pomocą funkcji infiksu using.

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

EnterTransition określa, jak mają wyglądać treści docelowe, a ExitTransition określa, jak mają zniknąć początkowe treści. Oprócz wszystkich funkcji EnterTransitionExitTransition dostępnych w AnimatedVisibility, AnimatedContent oferuje funkcje slideIntoContainerslideOutOfContainer. Są to wygodne alternatywy dla funkcji slideInHorizontally/VerticallyslideOutHorizontally/Vertically, które obliczają odległość slajdu na podstawie rozmiarów początkowych treści i docelowych treści treści AnimatedContent.

SizeTransform określa, jak rozmiar powinien się animować między początkową a docelową zawartością. Podczas tworzenia animacji masz dostęp zarówno do rozmiaru początkowego, jak i docelowego. SizeTransform określa też, czy podczas animacji treści mają być przycinane do rozmiaru komponentu.

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

Animowanie przejść rozpoczęcia i zakończenia elementów podrzędnych

Podobnie jak w przypadku AnimatedVisibility, modyfikator animateEnterExit jest dostępny w treściach lambda w AnimatedContent. Użyj tego, aby zastosować EnterAnimation i ExitAnimation do każdego bezpośredniego lub pośredniego podrzędnego obiektu osobno.

Dodawanie animacji niestandardowej

Tak jak AnimatedVisibility, pole transition jest dostępne w ramach funkcji lambda treści AnimatedContent. Użyj tego, aby utworzyć niestandardowy efekt animacji, który będzie działał jednocześnie z AnimatedContent. Więcej informacji znajdziesz w artykule updateTransition.

Animacja przejścia między dwoma układami za pomocą Crossfade

Crossfade przełącza się między 2 układami za pomocą animacji przejścia. Za pomocą przełącznika wartości przekazywanej do parametru current można przełączać zawartość za pomocą animacji przejścia.

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

Wbudowane modyfikatory animacji

Animowanie zmian rozmiaru za pomocą animateContentSize

Zielony element składany z animacją zmiany rozmiaru.
Rysunek 2. animacja płynnie przechodząca z mniejszego do większego rozmiaru

Modyfikator animateContentSize animuje zmianę rozmiaru.

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
        }

) {
}

Animacje elementów listy

Jeśli chcesz animować przetasowanie elementów na liście lub w siatkowaniu opartym na technologii Lazy, zapoznaj się z dokumentacją dotyczącą animacji elementów w ramach układu Lazy.

Obecnie nie ma rekomendacji.

na swoje konto Google.