Übergang zu gemeinsam genutzten Elementen anpassen

Mit einigen Parametern lässt sich anpassen, wie die Übergangsanimation für gemeinsame Elemente abläuft.

Animationsspezifikation

Wenn Sie die Animationsspezifikation für die Größen- und Positionsänderung ändern möchten, können Sie unter Modifier.sharedElement() einen anderen boundsTransform-Parameter angeben. So werden die Anfangs- und Zielposition von Rect angegeben.Rect

Wenn sich der Text im vorherigen Beispiel beispielsweise in einem Bogen bewegen soll, 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 eine beliebige AnimationSpec verwenden. In diesem Beispiel wird eine keyframes-Spezifikation verwendet.

Abbildung 1. Beispiel für verschiedene boundsTransform-Parameter

Modus zum Ändern der Größe

Wenn Sie zwischen zwei gemeinsamen Grenzen animieren, können Sie den Parameter resizeMode auf RemeasureToBounds oder ScaleToBounds festlegen. Mit diesem Parameter wird festgelegt, wie das freigegebene Element zwischen den beiden Status übergeht. ScaleToBounds misst zuerst das untergeordnete Layout mit den Vorschau- (oder Ziel-)Einschränkungen. Anschließend wird das stabile Layout des untergeordneten Elements so skaliert, dass es in die gemeinsamen Grenzen passt. ScaleToBounds kann als „grafische Skala“ zwischen den Zuständen betrachtet werden.

Bei RemeasureToBounds wird das untergeordnete Layout von sharedBounds mit animierten festen Einschränkungen basierend auf der Zielgröße neu gemessen und neu angeordnet. Die Neumessung wird durch die Änderung der Begrenzungsgröße ausgelöst, was potenziell bei jedem Frame der Fall sein kann.

Für Text-Kompositionen wird ScaleToBounds empfohlen, da dadurch ein Neulayout und ein Neufluss des Texts auf verschiedene Zeilen vermieden wird. Wenn die Grenzen unterschiedliche Seitenverhältnisse haben und Sie eine flüssige Kontinuität zwischen den beiden gemeinsamen Elementen wünschen, wird RemeasureToBounds empfohlen.

Die Unterschiede zwischen den beiden Modi zum Ändern der Größe sind in den folgenden Beispielen zu sehen:

ScaleToBounds

RemeasureToBounds

Zum endgültigen Layout springen

Beim Übergang zwischen zwei Layouts wird die Layoutgröße zwischen dem Start- und dem Endzustand standardmäßig animiert. Das kann bei der Animation von Inhalten wie Text unerwünscht sein.

Im folgenden Beispiel wird der Beschreibungstext „Lorem Ipsum“ auf zwei verschiedene Arten auf dem Bildschirm angezeigt. Im ersten Beispiel wird der Text umgebrochen, wenn er größer wird, wenn der Container größer wird. Im zweiten Beispiel wird der Text nicht automatisch angepasst, wenn er größer wird. Wenn Sie Modifier.skipToLookaheadSize() hinzufügen, wird das automatische Neuformatieren verhindert.

Ohne Modifier.skipToLookahead() – beachten Sie, dass der Text „Lorem Ipsum“ neu formatiert wird

Modifier.skipToLookahead(): Beachten Sie, dass der Text "Lorem Ipsum" zu Beginn der Animation seinen endgültigen Zustand beibehält.

Clips und Overlays

Ein wichtiges Konzept beim Erstellen gemeinsam genutzter Elemente in Compose ist, dass sie zwischen verschiedenen Compose-Elementen freigegeben werden können, wenn das Rendering des Compose-Elements in ein Ebenen-Overlay verschoben wird, wenn der Übergang zu seiner Übereinstimmung im Ziel gestartet wird. Das hat zur Folge, dass die Grenzen des übergeordneten Elements und seine Ebenentransformationen (z. B. Alpha und Skalierung) umgangen werden.

Es wird über anderen nicht geteilten UI-Elementen gerendert. Nach Abschluss des Übergangs wird das Element vom Overlay in sein eigenes DrawScope-Element verschoben.

Wenn Sie ein freigegebenes Element zu einer Form zuschneiden möchten, verwenden Sie die Standardfunktion Modifier.clip(). Platzieren Sie 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 ein freigegebenes Element niemals außerhalb eines übergeordneten Containers gerendert werden soll, können Sie clipInOverlayDuringTransition auf sharedElement() setzen. Standardmäßig verwendet clipInOverlayDuringTransition für verschachtelte gemeinsame Begrenzungen den Clippfad des übergeordneten sharedBounds().

Wenn Sie möchten, dass bestimmte UI-Elemente wie eine Leiste oder eine unverankerte Aktionsschaltfläche während eines Übergangs mit gemeinsam genutzten Elementen immer oben im Vordergrund bleiben, verwenden Sie Modifier.renderInSharedTransitionScopeOverlay(). Dieser Modifikator behält standardmäßig den Inhalt im Overlay bei, während der gemeinsame Übergang aktiv ist.

In Jetsnack muss BottomAppBar beispielsweise so lange auf dem gemeinsam genutzten Element platziert werden, bis der Bildschirm nicht mehr sichtbar ist. Wenn Sie den Modifier zum Composeable hinzufügen, bleibt er hervorgehoben.

Ohne Modifier.renderInSharedTransitionScopeOverlay()

Mit Modifier.renderInSharedTransitionScopeOverlay()

Manchmal möchten Sie, dass sich das nicht freigegebene Composeable wegbewegt und vor dem Übergang über den anderen Composeables bleibt. Verwenden Sie in solchen Fällen renderInSharedTransitionScopeOverlay().animateEnterExit(), um das Zusammenspiel der Elemente zu animieren, während die Überblendung des freigegebenen Elements ausgeführt wird:

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

Abbildung 2: Untere App-Leiste, die beim Übergang der Animation ein- und ausgeblendet wird

Falls Sie nicht möchten, dass ein geteiltes Element in einem Overlay gerendert wird, können Sie renderInOverlayDuringTransition auf sharedElement() auf „false“ setzen.

gleichgeordnete Layouts über Änderungen an gemeinsam genutzter Elementgröße benachrichtigen

Standardmäßig benachrichtigen sharedBounds() und sharedElement() den übergeordneten Container nicht über Größenänderungen während des Layoutübergangs.

Wenn Sie die Größenänderungen während der Übergänge an den übergeordneten Container weitergeben möchten, ä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.

PlaceholderSize.contentSize (Standard)

PlaceholderSize.animatedSize

Beachten Sie, wie die anderen Elemente in der Liste als Reaktion auf das wachsende Element nach unten verschoben werden.