Personnaliser la transition d'élément partagé

Pour personnaliser l'exécution de l'animation de transition d'élément partagé, qui peuvent être utilisés pour modifier la transition des éléments partagés.

Spécification d'animation

Pour modifier les spécifications d'animation utilisées pour le mouvement de taille et de position, vous pouvez spécifiez un autre paramètre boundsTransform sur Modifier.sharedElement(). Cela permet d'obtenir la position initiale Rect et la position Rect cible.

Par exemple, pour que le texte de l'exemple précédent se déplace selon un arc de mouvement, spécifiez le paramètre boundsTransform pour utiliser une spécification 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
    )
)

Vous pouvez utiliser n'importe quel AnimationSpec. Cet exemple utilise une spécification keyframes.

Figure 1. Exemple illustrant différents paramètres boundsTransform

Mode redimensionnement

Lors de l'animation entre deux limites partagées, vous pouvez définir le paramètre resizeMode sur RemeasureToBounds ou ScaleToBounds. Ce paramètre détermine la façon dont l'élément partagé passe d'un état à l'autre. ScaleToBounds mesure d'abord la mise en page enfant avec les contraintes d'anticipation (ou de cible). La mise en page stable de l'enfant est ensuite mise à l'échelle pour s'adapter aux limites partagées. ScaleToBounds peut être considéré comme une "échelle graphique" entre les états.

Alors que RemeasureToBounds remesure et remanie la mise en page enfant de sharedBounds avec des contraintes fixes animées en fonction de la taille cible. La nouvelle mesure est déclenchée par le changement de taille des limites, qui peut se produire à chaque frame.

Pour les composables Text, nous vous recommandons d'utiliser ScaleToBounds, car cela permet d'éviter la remise en page. et l'ajustement de la mise en page sur différentes lignes. Pour les limites dont les proportions sont différentes et si vous souhaitez une continuité fluide entre les deux éléments partagés, RemeasureToBounds est recommandé.

La différence entre les deux modes de redimensionnement est illustrée dans les exemples suivants:

ScaleToBounds

RemeasureToBounds

Passer à la mise en page finale

Par défaut, lors de la transition entre deux mises en page, la taille de la mise en page s'anime. entre son état de départ et son état final. Il peut s'agir d'un comportement indésirable pour animer du contenu, par exemple du texte.

L'exemple suivant illustre le texte de description "Lorem Ipsum" entrée l'écran de deux manières différentes. Dans le premier exemple, le texte est réorganisé à mesure qu'il est saisi et que la taille du conteneur augmente. Dans le second exemple, le texte n'est pas réorganisé à mesure qu'il augmente. L'ajout de Modifier.skipToLookaheadSize() empêche l'ajustement de la mise en page. à mesure qu'elle se développe.

Absence de Modifier.skipToLookahead() (notez la présence du "Lorem Ipsum") ajustement de la mise en page du texte

Modifier.skipToLookahead() : notez que le texte "Lorem Ipsum" conserve son état final au début de l'animation.

Extrait et superpositions

Un concept important lors de la création d'éléments partagés dans Compose est que, pour qu'ils puissent être partagés entre différents composables, le rendu du composable est élevé dans une superposition de calques lorsque la transition est lancée pour sa correspondance dans la destination. Cela a pour effet de s'échapper des limites du parent et de ses transformations de calque (par exemple, l'alpha et l'échelle).

Il s'affichera au-dessus des autres éléments d'interface utilisateur non partagés une fois la transition effectuée terminé, l'élément est supprimé de la superposition vers son propre DrawScope.

Pour découper un élément partagé selon une forme, utilisez la méthode Modifier.clip() standard . Placez-le après 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
)

Si vous devez vous assurer qu'un élément partagé ne s'affiche jamais en dehors d'un conteneur parent, vous pouvez définir clipInOverlayDuringTransition sur sharedElement(). Par par défaut, pour les limites imbriquées partagées, clipInOverlayDuringTransition utilise l'extrait chemin à partir de l'élément parent sharedBounds().

Pour que des éléments d'interface utilisateur spécifiques, tels qu'une barre inférieure ou un bouton d'action flottant, restent toujours en haut lors d'une transition d'élément partagé, utilisez Modifier.renderInSharedTransitionScopeOverlay(). Par défaut, ce modificateur conserve le contenu dans la superposition pendant la période où la transition partagée est active.

Par exemple, dans Jetsnack, BottomAppBar doit être placé au-dessus de l'élément partagé jusqu'à ce que l'écran ne soit plus visible. L'ajout du modificateur au composable le maintient en hauteur.

Sans Modifier.renderInSharedTransitionScopeOverlay()

Avec Modifier.renderInSharedTransitionScopeOverlay()

Il peut arriver que vous souhaitiez que votre composable non partagé s'anime et reste au-dessus des autres composables avant la transition. Dans ce cas, utilisez renderInSharedTransitionScopeOverlay().animateEnterExit() pour animer la sortie du composable pendant l'exécution de la transition d'élément partagé :

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

Figure 2 : Barre d'application inférieure qui glisse vers l'intérieur et vers l'extérieur lors des transitions d'animation

Dans les rares cas où vous souhaiteriez que l'élément partagé ne s'affiche pas dans un vous pouvez définir renderInOverlayDuringTransition sur sharedElement() sur "false".

Informer les mises en page sœurs des modifications apportées à la taille des éléments partagés

Par défaut, sharedBounds() et sharedElement() n'informent pas le conteneur parent de toute modification de taille lors des transitions de mise en page.

Pour propager les modifications de taille au conteneur parent lors de la transition, remplacez le paramètre placeHolderSize par PlaceHolderSize.animatedSize. Faire et entraîne donc une croissance ou une réduction de l'élément. Tous les autres éléments de la mise en page réagissent à ce changement.

PlaceholderSize.contentSize (par défaut)

PlaceholderSize.animatedSize

(Notez que les autres éléments de la liste descendent lorsqu'un élément augmente.)