Übergang zu gemeinsam genutzten Elementen anpassen

Um die Ausführung der Übergangsanimation mit gemeinsam genutzten Elementen anzupassen, gibt es einige Parameter, mit denen sich der Übergang der gemeinsam genutzten Elemente ändern lässt.

Animationsspezifikation

Wenn Sie die für die Größe und Positionsbewegung verwendete Animationsspezifikation ändern möchten, können Sie für Modifier.sharedElement() einen anderen boundsTransform-Parameter angeben. Dadurch werden die Ausgangsposition Rect und die Zielposition Rect angegeben.

Wenn der Text im vorherigen Beispiel beispielsweise mit einer Bogenbewegung bewegt werden soll, geben Sie den Parameter boundsTransform an, um eine keyframes-Spezifikation zu verwenden:

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 setzen. Dieser Parameter bestimmt, wie das gemeinsame Element zwischen den beiden Zuständen wechselt. ScaleToBounds misst zuerst das untergeordnete Layout mit den Lookahead-Einschränkungen (oder Zieleinschränkungen). Dann wird das stabile Layout des untergeordneten Elements so skaliert, dass es in die gemeinsamen Grenzen passt. ScaleToBounds kann als eine "grafische Skala" zwischen den Zuständen betrachtet werden.

Während RemeasureToBounds das untergeordnete Layout von sharedBounds mit animierten festen Einschränkungen basierend auf der Zielgröße noch einmal misst und neu erstellt. Die erneute Messung wird durch die Änderung der Begrenzungsgröße ausgelöst, die möglicherweise jeden Frame umfassen kann.

Für zusammensetzbare Text-Elemente wird ScaleToBounds empfohlen, da damit das Layout und der Umfluss von Text in verschiedene Zeilen vermieden werden. Für Grenzen mit unterschiedlichen Seitenverhältnissen und für eine flüssige Kontinuität zwischen den beiden gemeinsamen Elementen wird RemeasureToBounds empfohlen.

In den folgenden Beispielen sehen Sie den Unterschied zwischen den beiden Modi zur Größenanpassung:

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. Bei der Animation von Inhalten wie Text kann dies unerwünschtes Verhalten sein.

Das folgende Beispiel zeigt, wie der Beschreibungstext „Lorem Ipsum“ auf zwei verschiedene Arten in den Bildschirm gelangt. 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 ein dynamischer Umbruch verhindert, während er größer wird.

Kein Modifier.skipToLookahead() - sehen Sie, dass der Text "Lorem Ipsum" umfließt.

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 besteht darin, dass für die gemeinsame Nutzung durch verschiedene zusammensetzbare Funktionen das Rendering der zusammensetzbaren Funktion zu einem Ebenen-Overlay erhöht wird, wenn der Übergang zu dessen Übereinstimmung im Ziel beginnt. Dadurch werden die Grenzen des übergeordneten Elements und seine Ebenentransformationen (z. B. Alpha und Skalierung) maskiert.

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

Verwenden Sie die Standardfunktion Modifier.clip(), um ein gemeinsam genutztes Element auf eine Form zu kürzen. Platziere ihn nach 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 gemeinsam genutztes Element nie außerhalb eines übergeordneten Containers gerendert werden soll, können Sie clipInOverlayDuringTransition auf sharedElement() festlegen. Bei verschachtelten freigegebenen Grenzen verwendet clipInOverlayDuringTransition standardmäßig den Clippfad des übergeordneten sharedBounds().

Wenn Sie möchten, dass bestimmte UI-Elemente wie eine Leiste am unteren Rand oder eine unverankerte Aktionsschaltfläche während eines Übergangs mit gemeinsam genutzten Elementen immer oben 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. Durch Hinzufügen des Modifizierers zur zusammensetzbaren Funktion bleibt dieser erhöht.

Ohne Modifier.renderInSharedTransitionScopeOverlay()

Mit Modifier.renderInSharedTransitionScopeOverlay()

Manchmal möchten Sie vielleicht, dass die nicht freigegebene zusammensetzbare Funktion weg animiert wird und vor dem Übergang über die anderen zusammensetzbaren Funktionen gelegt wird. Verwenden Sie in diesen Fällen renderInSharedTransitionScopeOverlay().animateEnterExit(), um die zusammensetzbare Funktion zu animieren, während der Übergang mit gemeinsam genutzten Elementen 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 bei Layoutübergängen.

Damit Größenänderungen beim Übergang an den übergeordneten Container weitergegeben werden, ändern Sie den Parameter placeHolderSize in PlaceHolderSize.animatedSize. Dadurch vergrößert oder verkleinert sich das Element. 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.