Aby dostosować sposób działania animacji przejścia elementu udostępnionego, możesz użyć kilku parametrów, które pozwolą zmienić sposób przejścia elementów udostępnionych.
Specyfikacja animacji
Aby zmienić specyfikację animacji używaną do przesuwania rozmiaru i pozycji, możesz określić inny parametr boundsTransform w Modifier.sharedElement().
Określa on początkową pozycję Rect i docelową pozycję Rect.
Aby na przykład tekst w poprzednim przykładzie poruszał się po łuku
ruchu, określ parametr boundsTransform, aby używać 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 AnimationSpec. W tym przykładzie używamy specyfikacji keyframes.
boundsTransformTryb zmiany rozmiaru
Podczas animowania między 2 udostępnionymi granicami możesz ustawić parametr resizeMode na RemeasureToBounds lub ScaleToBounds. Ten parametr określa, jak element udostępniony przechodzi między 2 stanami. ScaleToBounds najpierw mierzy układ podrzędny z ograniczeniami lookahead (lub docelowymi). Następnie stabilny układ podrzędny jest skalowany tak, aby pasował do udostępnionych granic.
ScaleToBounds można traktować jako „skalę graficzną” między stanami.
Z kolei RemeasureToBounds ponownie mierzy i ponownie układa układ podrzędny sharedBounds z animowanymi stałymi ograniczeniami na podstawie rozmiaru docelowego. Ponowny pomiar jest wywoływany przez zmianę rozmiaru granic, która może występować w każdej klatce.
W przypadku elementów kompozycyjnych Text zalecamy używanie ScaleToBounds, ponieważ pozwala to uniknąć ponownego układania i przepływu tekstu do innych wierszy. RemeasureToBounds zalecamy w przypadku granic o różnych proporcjach oraz jeśli chcesz uzyskać płynną ciągłość między 2 elementami udostępnionymi.
Różnicę między 2 trybami zmiany rozmiaru można zobaczyć w przykładach poniżej:
|
|
|---|---|
Dynamiczne włączanie i wyłączanie elementów udostępnionych
Domyślnie sharedElement() i sharedBounds() są skonfigurowane tak, aby animować zmiany układu, gdy w stanie docelowym zostanie znaleziony pasujący klucz. Możesz jednak wyłączyć tę animację dynamicznie na podstawie określonych warunków, takich jak kierunek nawigacji lub bieżący stan interfejsu.
Aby kontrolować, czy ma nastąpić przejście elementu udostępnionego, możesz dostosować SharedContentConfig przekazywany do rememberSharedContentState(). Właściwość isEnabled określa, czy element udostępniony jest aktywny.
Poniższy przykład pokazuje, jak zdefiniować konfigurację, która włącza przejście udostępnione tylko podczas nawigacji między określonymi ekranami (np. tylko z ekranu A na ekran B), a wyłącza je w innych przypadkach.
SharedTransitionLayout { val transition = updateTransition(currentState) transition.AnimatedContent { targetState -> // Create the configuration that depends on state changing. fun animationConfig() : SharedTransitionScope.SharedContentConfig { return object : SharedTransitionScope.SharedContentConfig { override val SharedTransitionScope.SharedContentState.isEnabled: Boolean // For this example, we only enable the transition in one direction // from A -> B and not the other way around. get() = transition.currentState == "A" && transition.targetState == "B" } } when (targetState) { "A" -> Box( modifier = Modifier .sharedElement( rememberSharedContentState( key = "shared_box", config = animationConfig() ), animatedVisibilityScope = this ) // ... ) { // Your content } "B" -> { Box( modifier = Modifier .sharedElement( rememberSharedContentState( key = "shared_box", config = animationConfig() ), animatedVisibilityScope = this ) // ... ) { // Your content } } } } }
Domyślnie, jeśli element udostępniony zostanie wyłączony podczas trwającej animacji, animacja zostanie dokończona, aby zapobiec przypadkowemu usunięciu animacji w trakcie. Jeśli musisz usunąć element podczas trwania animacji, możesz zastąpić shouldKeepEnabledForOngoingAnimation w interfejsie SharedContentConfig, aby zwracać wartość false.
Przejdź do układu końcowego
Domyślnie podczas przechodzenia między 2 układami rozmiar układu jest animowany między stanem początkowym a końcowym. Może to być niepożądane zachowanie podczas animowania treści, takich jak tekst.
Poniższy przykład ilustruje tekst opisu „Lorem Ipsum” pojawiający się na ekranie na 2 różne sposoby. W pierwszym przykładzie tekst jest ponownie układany, gdy kontener zwiększa swój rozmiar. W drugim przykładzie tekst nie jest ponownie układany, gdy zwiększa się jego rozmiar. Dodanie Modifier.skipToLookaheadSize() zapobiega ponownemu układaniu tekstu podczas jego powiększania.
Brak |
|
|---|---|
Przycinanie i nakładki
Aby elementy udostępnione mogły być udostępniane między różnymi elementami kompozycyjnymi, renderowanie elementu kompozycyjnego jest przenoszone do nakładki warstwy, gdy przejście rozpoczyna się do jego odpowiednika w miejscu docelowym. Powoduje to, że element kompozycyjny wykracza poza granice elementu nadrzędnego i jego transformacje warstwy (np. alfa i skala).
Będzie renderowany na wierzchu innych nieudostępnionych elementów interfejsu. Po zakończeniu przejścia element zostanie usunięty z nakładki do własnego DrawScope.
Aby przyciąć element udostępniony do kształtu, użyj standardowej funkcji Modifier.clip(). Umieść ją 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 element udostępniony nigdy nie będzie renderowany poza kontenerem nadrzędnym, możesz ustawić clipInOverlayDuringTransition w sharedElement(). Domyślnie w przypadku zagnieżdżonych granic udostępnionych clipInOverlayDuringTransition używa ścieżki przycinania z elementu nadrzędnego sharedBounds().
Aby podczas przejścia elementu udostępnionego zachować na wierzchu określone elementy interfejsu, takie jak pasek u dołu ekranu lub pływający przycisk czynności
, użyj
Modifier.renderInSharedTransitionScopeOverlay(). Domyślnie ten modyfikator utrzymuje treść w nakładce, gdy przejście udostępnione jest aktywne.
Na przykład w Jetsnack BottomAppBar musi być umieszczony na wierzchu elementu udostępnionego, dopóki ekran nie będzie widoczny. Dodanie modyfikatora do elementu kompozycyjnego powoduje, że pozostaje on na wierzchu.
Bez |
Z |
|---|---|
Możesz chcieć, aby element kompozycyjny nieudostępniony również był animowany, a także pozostawał na wierzchu innych elementów kompozycyjnych przed przejściem. W takich przypadkach użyj renderInSharedTransitionScopeOverlay().animateEnterExit(), aby animować element kompozycyjny podczas przejścia elementu udostępnionego:
JetsnackBottomBar( modifier = Modifier .renderInSharedTransitionScopeOverlay( zIndexInOverlay = 1f, ) .animateEnterExit( enter = fadeIn() + slideInVertically { it }, exit = fadeOut() + slideOutVertically { it } ) )
W rzadkich przypadkach, gdy nie chcesz, aby element udostępniony był renderowany w nakładce, możesz ustawić renderInOverlayDuringTransition w sharedElement() na false.
Powiadamianie układów równorzędnych o zmianach rozmiaru elementu udostępnionego
Domyślnie sharedBounds() i sharedElement() nie powiadamiają kontenera nadrzędnego o żadnych zmianach rozmiaru podczas przejścia układu.
Aby propagować zmiany rozmiaru do kontenera nadrzędnego podczas przejścia, zmień parametr placeholderSize na PlaceholderSize.AnimatedSize. Spowoduje to powiększenie lub pomniejszenie elementu. Wszystkie inne elementy w układzie reagują na tę zmianę.
|
(Zwróć uwagę, jak inne elementy na liście przesuwają się w dół w odpowiedzi na powiększenie jednego elementu) |
|---|---|