Dostosuj przejście elementów wspólnych

Aby dostosować sposób działania animacji wspólnych elementów, możesz użyć kilku parametrów do zmiany sposobu ich przenoszenia.

Specyfikacja animacji

Aby zmienić specyfikację animacji używaną przy przesuwaniu rozmiaru i pozycji, możesz podać inny parametr boundsTransform w elemencie Modifier.sharedElement(). Podaje początkową pozycję Rect i pozycję docelową Rect.

Aby np. aby tekst z poprzedniego przykładu poruszał się po łuku, określ parametr boundsTransform, by zastosować specyfikację keyframes:

val textBoundsTransform = BoundsTransform { initialBounds, targetBounds ->
    keyframes {
        durationMillis = boundsAnimationDurationMillis
        initialBounds at 0 using ArcMode.ArcBelow using FastOutSlowInEasing
        targetBounds at boundsAnimationDurationMillis
    }
}
Text(
    "Cupcake", fontSize = 28.sp,
    modifier = Modifier.sharedBounds(
        rememberSharedContentState(key = "title"),
        animatedVisibilityScope = animatedVisibilityScope,
        boundsTransform = textBoundsTransform
    )
)

Możesz użyć dowolnego elementu AnimationSpec. W tym przykładzie używamy specyfikacji keyframes.

Rysunek 1. Przykład przedstawiający różne parametry boundsTransform

Tryb zmiany rozmiaru

Podczas animowania między 2 wspólnymi granicami możesz ustawić parametr resizeMode na RemeasureToBounds lub ScaleToBounds. Ten parametr określa, w jaki sposób wspólny element przechodzi między tymi dwoma stanami. ScaleToBounds mierzy najpierw układ podrzędny z zastosowaniem ograniczeń „wyprzedzeniem” (lub „cel”). Następnie stabilny układ elementu podrzędnego jest skalowany, by dopasować go do wspólnych granic. ScaleToBounds to rodzaj „skali graficznej” między stanami.

Parametr RemeasureToBounds ponownie mierzy i przekształca układ podrzędny elementu sharedBounds z animowanymi ograniczeniami stałymi na podstawie rozmiaru docelowego. Ponowny pomiar jest wywoływany przez zmianę rozmiaru granic, która może dotyczyć każdej klatki.

W przypadku funkcji kompozycyjnych Text zalecany jest format ScaleToBounds, ponieważ pozwala to uniknąć przekazywania i przekładania tekstu na inne wiersze. W przypadku granic o różnych współczynnikach proporcji oraz jeśli chcesz zachować płynną ciągłość między 2 wspólnymi elementami, zalecamy użycie właściwości RemeasureToBounds.

Różnice między tymi 2 trybami zmiany rozmiaru można zobaczyć w poniższych przykładach:

ScaleToBounds

RemeasureToBounds

Przejdź do ostatecznego układu

Domyślnie podczas przechodzenia między 2 układami rozmiar układu animuje się między stanem początkowym a końcowym. Może to być niepożądane zachowanie podczas animowania treści takich jak tekst.

W przykładzie poniżej widać, jak tekst opisu „Lorem Ipsum” pojawia się na ekranie na 2 różne sposoby. W pierwszym przykładzie tekst zmienia się w miarę zwiększania rozmiaru kontenera, a w drugim – jego układ nie zmienia się wraz ze wzrostem. Dodanie parametru Modifier.skipToLookaheadSize() zapobiega przeformatowaniu w miarę jego wzrostu.

Brak parametru Modifier.skipToLookahead() – zwróć uwagę na zmianę układu tekstu „Lorem Ipsum”.

Modifier.skipToLookahead() – zwróć uwagę, że tekst „Lorem Ipsum” zachowuje swój końcowy stan na początku animacji.

Klip i nakładki

Ważnym aspektem podczas tworzenia wspólnych elementów w funkcji Utwórz jest to, że aby mogły one współdzielić różne elementy kompozycyjne, renderowanie elementu kompozycyjnego jest podnoszone do nakładki warstwy, gdy przejście zaczyna się do niej pasować w miejscu docelowym. Skutkiem jest to, że poza granicami jednostki nadrzędnej i przekształceniami jej warstwy (np. alfa i skalę).

Będzie się wyświetlać nad innymi nieudostępnionymi elementami interfejsu, a po zakończeniu przejścia element zostanie usunięty z nakładki do własnego elementu DrawScope.

Aby przyciąć udostępniony element do kształtu, użyj standardowej funkcji Modifier.clip(). Umieść go po kolumnie sharedElement():

Image(
    painter = painterResource(id = R.drawable.cupcake),
    contentDescription = "Cupcake",
    modifier = Modifier
        .size(100.dp)
        .sharedElement(
            rememberSharedContentState(key = "image"),
            animatedVisibilityScope = this@AnimatedContent
        )
        .clip(RoundedCornerShape(16.dp)),
    contentScale = ContentScale.Crop
)

Jeśli chcesz mieć pewność, że udostępniany element nigdy nie będzie renderowany poza kontenerem nadrzędnym, możesz ustawić clipInOverlayDuringTransition w polu sharedElement(). Domyślnie w przypadku zagnieżdżonych wspólnych granic clipInOverlayDuringTransition używa ścieżki klipu z elementu nadrzędnego sharedBounds().

Aby ułatwić pozostawianie określonych elementów interfejsu, takich jak dolny pasek czy pływający przycisk polecenia, zawsze na górze podczas przejścia elementu współdzielonego, użyj właściwości Modifier.renderInSharedTransitionScopeOverlay(). Domyślnie ten modyfikator zachowuje treść nakładek w czasie, gdy udostępnione przejście jest aktywne.

Na przykład w Jtsnacku trzeba umieszczać BottomAppBar nad udostępnionym elementem, aż do momentu, gdy ekran przestanie być widoczny. Dodanie modyfikatora do funkcji kompozycyjnej sprawia, że zostanie on podniesiony.

Bez: Modifier.renderInSharedTransitionScopeOverlay()

Przez: Modifier.renderInSharedTransitionScopeOverlay()

Czasami możesz chcieć, aby nieudostępniany element kompozycyjny był animowany i pozostał nad innymi elementami kompozycyjnymi przed przeniesieniem. W takich przypadkach użyj funkcji renderInSharedTransitionScopeOverlay().animateEnterExit(), aby animować obiekt kompozycyjny w trakcie przenoszenia elementów wspólnych:

JetsnackBottomBar(
    modifier = Modifier
        .renderInSharedTransitionScopeOverlay(
            zIndexInOverlay = 1f,
        )
        .animateEnterExit(
            enter = fadeIn() + slideInVertically {
                it
            },
            exit = fadeOut() + slideOutVertically {
                it
            }
        )
)

Rys. 2.Wysuwany dolny pasek aplikacji podczas przechodzenia animacji

W rzadkich przypadkach, gdy nie chcesz, by udostępniony element był renderowany w nakładce, możesz ustawić właściwość renderInOverlayDuringTransition w polu sharedElement() na wartość fałsz.

Powiadamianie układów równorzędnych o zmianach rozmiaru udostępnianego elementu

Domyślnie sharedBounds() i sharedElement() nie powiadamiają kontenera nadrzędnego o zmianie rozmiaru podczas przenoszenia układu.

Aby przekazywać zmiany rozmiaru w kontenerze nadrzędnym podczas jego przenoszenia, zmień parametr placeHolderSize na PlaceHolderSize.animatedSize. W ten sposób element powiększy się lub zmniejsz. Pozostałe elementy układu reagują na tę zmianę.

PlaceholderSize.contentSize (domyślnie)

PlaceholderSize.animatedSize

(Zwróć uwagę, jak inne elementy na liście przesuwają się w dół w odpowiedzi na rosnący element).