Dostosuj przejście elementów wspólnych

Aby dostosować sposób wyświetlania animacji przejścia elementów udostępnionych, możesz użyć kilku parametrów, które pozwalają zmienić sposób przejścia tych elementów.

Specyfikacja animacji

Aby zmienić specyfikację animacji używaną w przypadku zmiany rozmiaru i pozycji, możesz w elemencie Modifier.sharedElement() podać inny parametr boundsTransform. Ta wartość określa początkową pozycję Rect i docelową Rect.

Aby np. tekst z poprzedniego przykładu poruszał się po łuku, określ parametr boundsTransform, by użyć specyfikacji 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ć dowolnej wartości AnimationSpec. W tym przykładzie użyto specyfikacji keyframes.

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

Tryb zmiany rozmiaru

Podczas animowania między 2 wspólnymi granicami parametr resizeMode możesz ustawić na RemeasureToBounds lub ScaleToBounds. Ten parametr określa, jak udostępniany element przechodzi między 2 stanami. ScaleToBounds najpierw mierzy układ podrzędny z ograniczeniami z wyprzedzeniem (lub elementami docelowymi). Następnie stabilny układ dziecka zostaje przeskalowany, aby mieścił się we wspólnych granicach. Element ScaleToBounds można określić jako „skalę graficzną” między stanami.

Z kolei RemeasureToBounds ponownie mierzy i przekazuje układ podrzędny elementu sharedBounds z animowanymi stałymi ograniczeniami na podstawie rozmiaru docelowego. Ponowny pomiar jest wyzwalany przez zmianę rozmiaru granic, która może obejmować każdą klatkę.

W przypadku funkcji kompozycyjnych Text zalecamy użycie ScaleToBounds, ponieważ zapobiega on przekazywaniu i przekazywaniu tekstu w różnych wierszach. W przypadku granic o różnych współczynnikach proporcji lub jeśli zależy Ci na płynności między dwoma wspólnymi elementami, zalecamy użycie właściwości RemeasureToBounds.

Różnica między tymi dwoma trybami widać w przykładach poniżej:

ScaleToBounds

RemeasureToBounds

Przejdź do ostatecznego układu

Domyślnie przy przechodzeniu między 2 układami rozmiar układu jest animowany między stanem początkowym i końcowym. Może to być niepożądane, gdy animujesz treści (np. tekst).

Poniższy przykład pokazuje tekst opisu „Lorem Ipsum” wchodzący na ekran na 2 różne sposoby. W pierwszym przykładzie tekst zmienia się wraz ze wzrostem rozmiaru kontenera, a w drugim – tekst nie zmienia się wraz ze wzrostem. Dodanie dyrektywy Modifier.skipToLookaheadSize() zapobiega ponownemu przepływowi w miarę jego wzrostu.

Brak Modifier.skipToLookahead() – zauważ zmianę przepływu tekstu „Lorem Ipsum”

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

Klip i nakładki

Istotną koncepcją przy tworzeniu udostępnionych elementów w funkcji Compose jest to, że aby mogły być one współużytkowane między różnymi elementami kompozycyjnymi, po rozpoczęciu przejścia do miejsca docelowego renderowanie elementu kompozycyjnego jest podwyższone do poziomu nakładki warstwy. W efekcie wykroczy poza granice obiektu nadrzędnego i przekształcenia warstw (np. alfa i skala).

Będzie się wyświetlał nad innymi nieudostępnionymi elementami interfejsu. 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 za elementem 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ępniony element nigdy nie będzie renderowany poza kontenerem nadrzędnym, możesz ustawić clipInOverlayDuringTransition w elemencie sharedElement(). Domyślnie w przypadku zagnieżdżonych wspólnych granic clipInOverlayDuringTransition używa ścieżki klipu z elementu nadrzędnego sharedBounds().

Aby określone elementy interfejsu, takie jak dolny pasek czy pływający przycisk polecenia, pozostawały zawsze na górze podczas przejścia do udostępnianego elementu, użyj właściwości Modifier.renderInSharedTransitionScopeOverlay(). Domyślnie ten modyfikator utrzymuje treść w nakładce w czasie, gdy współdzielone przejście jest aktywne.

Na przykład w Jetnack obiekt BottomAppBar musi być umieszczony na udostępnianym elemencie do momentu, gdy ekran nie będzie widoczny. Dodanie modyfikatora do funkcji kompozycyjnej sprawia, że element jest wyższy.

Bez: Modifier.renderInSharedTransitionScopeOverlay()

Przez: Modifier.renderInSharedTransitionScopeOverlay()

Czasami możesz chcieć, aby Twoja nieudostępniona funkcja kompozycyjna była animowana poza innymi elementami kompozycyjnymi przed przejściem. W takich przypadkach użyj renderInSharedTransitionScopeOverlay().animateEnterExit(), aby animować element kompozycyjny w miarę wykonywania przejścia ze wspólnego elementu:

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

Rys. 2. Dolny pasek aplikacji przesuwający się w górę i z poziomu animacji

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

Powiadamianie układów równorzędnych o zmianach rozmiaru udostępnianych elementów

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

Aby zastosować zmiany rozmiaru do kontenera nadrzędnego podczas przenoszenia, zmień parametr placeHolderSize na PlaceHolderSize.animatedSize. Powoduje to powiększanie lub pomniejszanie elementu. Pozostałe elementy układu reagują na tę zmianę.

PlaceholderSize.contentSize (domyślnie)

PlaceholderSize.animatedSize

(Zwróć uwagę, jak pozostałe elementy na liście przesuwają się w dół w odpowiedzi na rosnącą pozycję jednego elementu).