Es gibt einige Parameter, mit denen Sie anpassen können, wie die Animation für den Übergang des gemeinsamen Elements abläuft.
Animationsspezifikation
Wenn Sie die für die Größen- und Positionsbewegung verwendete Animationsspezifikation ändern möchten, können Sie in Modifier.sharedElement() einen anderen boundsTransform-Parameter angeben.
So werden die ursprüngliche Rect-Position und die Ziel-Rect-Position angegeben.
Wenn Sie beispielsweise möchten, dass sich der Text im vorherigen Beispiel in einem Bogen bewegt, geben Sie für den Parameter boundsTransform eine keyframes-Spezifikation an:
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 ) )
Sie können einen beliebigen AnimationSpec verwenden. In diesem Beispiel wird eine keyframes-Spezifikation verwendet.
boundsTransform-ParameterModus zum Ändern der Größe
Wenn Sie eine Animation zwischen zwei gemeinsamen Grenzen erstellen, können Sie den Parameter resizeMode entweder auf RemeasureToBounds oder auf ScaleToBounds festlegen. Mit diesem Parameter wird festgelegt, wie das gemeinsame Element zwischen den beiden Status übergeht. ScaleToBounds first misst das untergeordnete Layout mit den Lookahead- oder Zielbeschränkungen. Das stabile Layout des untergeordneten Elements wird dann so skaliert, dass es in die gemeinsamen Grenzen passt.
ScaleToBounds kann als „grafische Skala“ zwischen den Status betrachtet werden.
Im Gegensatz dazu werden bei RemeasureToBounds das untergeordnete Layout von sharedBounds mit animierten festen Einschränkungen basierend auf der Zielgröße neu gemessen und neu angeordnet. Die erneute Messung wird durch die Änderung der Begrenzungsgröße ausgelöst, was potenziell in jedem Frame geschehen kann.
Für Text-Composables wird ScaleToBounds empfohlen, da dadurch ein erneutes Layout und ein Umfließen von Text in andere Zeilen vermieden wird. RemeasureToBounds wird für Grenzen mit unterschiedlichen Seitenverhältnissen empfohlen und wenn Sie einen fließenden Übergang zwischen den beiden gemeinsamen Elementen wünschen.
Der Unterschied zwischen den beiden Größenanpassungsmodi ist in den folgenden Beispielen zu sehen:
|
|
|---|---|
Gemeinsam genutzte Elemente dynamisch aktivieren und deaktivieren
Standardmäßig sind sharedElement() und sharedBounds() so konfiguriert, dass Layoutänderungen animiert werden, wenn im Zielstatus ein übereinstimmender Schlüssel gefunden wird. Möglicherweise möchten Sie diese Animation jedoch dynamisch deaktivieren, z. B. basierend auf der Navigationsrichtung oder dem aktuellen UI-Status.
Sie können festlegen, ob die Übergangsanimation für das gemeinsame Element stattfindet, indem Sie das an rememberSharedContentState() übergebene SharedContentConfig anpassen. Mit der Property isEnabled wird festgelegt, ob das gemeinsame Element aktiv ist.
Im folgenden Beispiel wird gezeigt, wie Sie eine Konfiguration definieren, die den gemeinsamen Übergang nur beim Navigieren zwischen bestimmten Bildschirmen (z.B. nur von A nach B) aktiviert, während er für andere deaktiviert wird.
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 } } } } }
Wenn ein gemeinsames Element während einer laufenden Animation deaktiviert wird, wird die aktuelle Animation standardmäßig trotzdem abgeschlossen, um zu verhindern, dass laufende Animationen versehentlich entfernt werden. Wenn Sie das Element entfernen müssen, während die Animation läuft, können Sie shouldKeepEnabledForOngoingAnimation in der SharedContentConfig-Schnittstelle überschreiben, um „false“ zurückzugeben.
Zum endgültigen Layout springen
Standardmäßig wird die Layoutgröße beim Übergang zwischen zwei Layouts zwischen dem Start- und dem Endstatus animiert. Das kann unerwünscht sein, wenn Sie Inhalte wie Text animieren.
Im folgenden Beispiel wird der Beschreibungstext „Lorem Ipsum“ auf zwei verschiedene Arten auf dem Bildschirm angezeigt. Im ersten Beispiel wird der Text umgebrochen, wenn der Container größer wird. Im zweiten Beispiel wird der Text nicht umgebrochen, wenn er länger wird. Durch das Hinzufügen von Modifier.skipToLookaheadSize() wird verhindert, dass sich das Layout ändert, wenn das Element größer wird.
Kein |
|
|---|---|
Clips und Overlays
Damit sich freigegebene Elemente zwischen verschiedenen Composables teilen können, wird das Rendern des Composables in eine Layer-Überlagerung verschoben, wenn die Übergangsanimation zum entsprechenden Element im Ziel gestartet wird. Dadurch wird die Ebene außerhalb der Grenzen des übergeordneten Elements und seiner Ebenentransformationen (z. B. Alpha und Skalierung) gerendert.
Es wird über anderen nicht freigegebenen UI-Elementen gerendert. Sobald die Übergangsphase abgeschlossen ist, wird das Element aus dem Overlay in einen eigenen DrawScope verschoben.
Wenn Sie ein gemeinsames Element auf eine Form zuschneiden möchten, verwenden Sie die Standardfunktion Modifier.clip(). Platziere es nach dem 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 )
Wenn Sie sicherstellen möchten, dass ein freigegebenes Element nie außerhalb eines übergeordneten Containers gerendert wird, können Sie clipInOverlayDuringTransition für sharedElement() festlegen. Standardmäßig wird für verschachtelte freigegebene Grenzen in clipInOverlayDuringTransition der Beschneidungspfad des übergeordneten sharedBounds() verwendet.
Wenn bestimmte UI-Elemente wie eine untere Leiste oder eine Floating Action Button während einer Übergangsanimation für gemeinsame Elemente immer oben bleiben sollen, verwenden Sie Modifier.renderInSharedTransitionScopeOverlay(). Standardmäßig wird mit diesem Modifikator der Inhalt im Overlay während der aktiven gemeinsamen Übergangszeit beibehalten.
In Jetsnack muss das BottomAppBar beispielsweise über dem freigegebenen Element platziert werden, bis der Bildschirm nicht mehr sichtbar ist. Wenn Sie den Modifier dem Composable hinzufügen, bleibt er erhöht.
Ohne |
Mit |
|---|---|
Möglicherweise soll das nicht freigegebene Composable vor dem Übergang animiert werden und über den anderen Composables bleiben. Verwenden Sie in solchen Fällen renderInSharedTransitionScopeOverlay().animateEnterExit(), um die Composable-Funktion während der Übergangsanimation des gemeinsamen Elements zu animieren:
JetsnackBottomBar( modifier = Modifier .renderInSharedTransitionScopeOverlay( zIndexInOverlay = 1f, ) .animateEnterExit( enter = fadeIn() + slideInVertically { it }, exit = fadeOut() + slideOutVertically { it } ) )
In seltenen Fällen möchten Sie vielleicht nicht, dass Ihr gemeinsames Element in einem Overlay gerendert wird. In diesem Fall können Sie renderInOverlayDuringTransition für sharedElement() auf „false“ setzen.
Benachrichtigen von untergeordneten Layouts über Änderungen an der Größe des freigegebenen Elements
Standardmäßig werden sharedBounds() und sharedElement() nicht über Größenänderungen des übergeordneten Containers während des Layoutübergangs informiert.
Wenn Größenänderungen während der Übergangsphase auf den übergeordneten Container übertragen werden sollen, ändern Sie den Parameter placeHolderSize in PlaceHolderSize.animatedSize. Dadurch wird das Element vergrößert oder verkleinert. Alle anderen Elemente im Layout reagieren auf die Änderung.
|
Beachten Sie, wie sich die anderen Elemente in der Liste nach unten verschieben, wenn ein Element größer wird. |
|---|---|