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, które umożliwiają zmianę sposobu przejścia.

Specyfikacja animacji

Aby zmienić specyfikację animacji używaną do zmiany rozmiaru i pozycji, możesz podać inny parametr boundsTransform w pliku Modifier.sharedElement(). To zapewnia początkową pozycję Rect i docelową pozycję Rect.

Aby na przykład tekst w poprzednim przykładzie poruszał się po łuku, użyj parametru boundsTransform, aby 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ć dowolnego elementu AnimationSpec. W tym przykładzie użyto specyfikacji keyframes.

Rysunek 1. Przykład pokazujący różne boundsTransformparametry

Tryb zmiany rozmiaru

Podczas animacji między 2 współdzielonemi granicami możesz ustawić parametr resizeMode 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ń wyprzedzających (lub docelowych). Następnie stabilny układ podrzędnego elementu jest skalowany, aby zmieścić się w ramach współdzielonych. ScaleToBounds można traktować jako „skala graficzna” stanów.

Natomiast RemeasureToBounds ponownie mierzy i układa układ podrzędnego komponentu sharedBounds z animowanymi stałymi ograniczeniami na podstawie docelowego rozmiaru. Ponowne pomiary są wywoływane przez zmianę rozmiaru granic, która może potencjalnie wystąpić w każdej klatce.

W przypadku komponentów Text zalecamy użycie ScaleToBounds, ponieważ zapobiega to ponownemu układaniu i przepływaniu tekstu na inne wiersze. RemeasureToBounds jest zalecana w przypadku ramek o różnych współczynnikach proporcji, jeśli chcesz uzyskać płynne przejście między 2 elementami współdzielonymi.

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 podczas przechodzenia między 2 układami rozmiar układu przechodzi od stanu początkowego do końcowego. 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. W drugim przykładzie tekst nie jest przeformatowywany, gdy się wydłuża. Dodanie Modifier.skipToLookaheadSize() zapobiega przepływowi treści na inne wiersze w miarę zwiększania się ich liczby.

Nie Modifier.skipToLookahead() – zwróć uwagę na przepływ 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

Aby udostępnione elementy mogły być udostępniane między różnymi komponentami, renderowanie komponentu jest podwyższane do nakładki warstwy, gdy rozpoczyna się przejście do dopasowania w miejscu docelowym. W efekcie element ten wyjdzie poza granice elementu nadrzędnego i jego przekształceń warstwy (np. alfa i skalowanie).

Będzie on renderowany na wierzchu innych elementów interfejsu, które nie są współdzielone. Po zakończeniu przejścia element zostanie przeniesiony z nakładki do własnego 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 zostanie wyrenderowany poza nadrzędnym kontenerem, możesz ustawić clipInOverlayDuringTransitionsharedElement(). Domyślnie w przypadku zaokrąglonych zakresów współdzielonych clipInOverlayDuringTransition używa ścieżki klipu z 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 elementu 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 kompozytowego powoduje, że pozostaje on na wierzchu.

Bez Modifier.renderInSharedTransitionScopeOverlay()

Przez: Modifier.renderInSharedTransitionScopeOverlay()

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

Rysunek 2. Dolny pasek aplikacji przesuwa się w górę i w dół podczas animacji przejść.

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 nadrzędnych o zmianach rozmiaru elementu współdzielonego

Domyślnie sharedBounds()sharedElement() nie informują kontenera nadrzędnego o żadnych zmianach rozmiaru podczas przechodzenia między układami.

Aby rozpowszechnić zmiany rozmiaru w kontenerze nadrzędnym podczas przejścia, 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)