Dostosuj przejście elementów wspólnych

Aby dostosować sposób działania animacji przejścia elementu współdzielonego, możesz użyć kilku parametró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. W ten sposób określasz początkową pozycję Rect i docelową pozycję Rect.

Jeśli np. tekst z poprzedniego przykładu ma 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 sposób przechodzenia elementu wspólnego między tymi dwoma stanami. ScaleToBounds najpierw mierzy układ podrzędny z zastosowaniem ograniczeń wstecznych (lub docelowych). Następnie stabilny układ podrzędnego elementu jest skalowany, aby zmieścić się w ramach współdzielonych. Element ScaleToBounds można określić jako „skalę graficzną” między stanami.

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

W przypadku komponentów Text zalecamy użycie ScaleToBounds, ponieważ zapobiegnie to ponownemu rozmieszczaniu i przepływaniu tekstu na inne wiersze. Jeśli chcesz uzyskać płynne przejście między 2 elementami, których współczynniki proporcji są różne, zalecamy użycie RemeasureToBounds.

Różnicę między tymi dwoma trybami zmiany rozmiaru widać w poniższych przykładach:

ScaleToBounds

RemeasureToBounds

Przejdź do układu końcowego

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 zachowanie podczas animowania treści, takich jak tekst.

W tym przykładzie tekst „Lorem Ipsum” pojawia się na ekranie na 2 sposoby. W pierwszym przykładzie tekst jest przeformatowywany, gdy wchodzi do kontenera, który zwiększa swój rozmiar, a w drugim przykładzie tekst nie jest przeformatowywany, gdy rośnie. Dodanie Modifier.skipToLookaheadSize() zapobiega przepływowi treści na inne wiersze w miarę zwiększania się ich liczby.

Bez modyfikatora skipToLookahead() – zwróć uwagę na przepływ tekstu „Lorem Ipsum”

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

Klip i nakładki

Podczas tworzenia elementów współdzielonych w komponencie należy pamiętać, że aby można było je udostępniać między różnymi komponentami, renderowanie komponentu jest podnoszone do nakładki warstwy, gdy rozpoczyna się przejście do jego odpowiednika na stronie docelowej. W efekcie wykroczy poza granice obiektu nadrzędnego i przekształcenia warstwy (np. alfa i skala).

Element będzie renderowany na wierzchu innych nieudostępnionych elementów interfejsu. Po zakończeniu przejścia element zostanie przeniesiony z nakładki do własnej DrawScope.

Aby przyciąć udostępniony element do kształtu, użyj standardowej funkcji Modifier.clip(). Umieść go po 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 zakresów clipInOverlayDuringTransition używa ścieżki klipu z folderu nadrzędnego sharedBounds().

Aby zapewnić, że określone elementy interfejsu, np. dolna belka lub pływający przycisk, będą zawsze widoczne u góry podczas przejścia z użyciem elementu współdzielonego, użyj Modifier.renderInSharedTransitionScopeOverlay(). Domyślnie ten modyfikator zachowuje zawartość nakładki przez cały czas trwania wspólnej animacji przejścia.

Na przykład w Jetsnackie element BottomAppBar musi być umieszczony na szczycie elementu współdzielonego, dopóki ekran nie stanie się niewidoczny. Dodanie modyfikatora do składanego elementu powoduje, że pozostaje on podniesiony.

Bez: Modifier.renderInSharedTransitionScopeOverlay()

Przez: Modifier.renderInSharedTransitionScopeOverlay()

Czasami możesz chcieć, aby nieudostępnione komponenty były animowane i pozostawały na wierzchu innych komponentów przed przejściem. W takich przypadkach użyj funkcji renderInSharedTransitionScopeOverlay().animateEnterExit(), aby animować kompozyt podczas przejścia elementu współdzielonego:

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 w dół podczas animacji

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

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

Domyślnie sharedBounds()sharedElement() nie powiadamią kontenera nadrzędnego o żadnych zmianach rozmiaru podczas przejść w ramach układu.

Aby zastosować zmiany rozmiaru do kontenera nadrzędnego podczas przenoszenia, zmień parametr placeHolderSize na PlaceHolderSize.animatedSize. Spowoduje to powiększenie lub pomniejszenie elementu. Wszystkie inne 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 wzrost jednego elementu)