Personalizza la transizione degli elementi condivisi

Per personalizzare l'esecuzione dell'animazione di transizione degli elementi condivisi, esistono alcuni parametri che possono essere utilizzati per modificare la transizione degli elementi condivisi.

Specifiche animazione

Per modificare le specifiche di animazione utilizzate per il movimento delle dimensioni e della posizione, puoi specificare un parametro boundsTransform diverso su Modifier.sharedElement(). In questo modo vengono fornite la posizione iniziale Rect e la posizione target Rect.

Ad esempio, per fare in modo che il testo nell'esempio precedente si muova con un movimento a forma di arco, specifica il parametro boundsTransform per utilizzare una specifica 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
    )
)

Puoi utilizzare qualsiasi AnimationSpec. Questo esempio utilizza una specifica keyframes.

Figura 1. Esempio che mostra diversi boundsTransformparametri

Modalità di ridimensionamento

Quando esegui l'animazione tra due limiti condivisi, puoi impostare il parametro resizeMode su RemeasureToBounds o ScaleToBounds. Questo parametro determina il modo in cui l'elemento condiviso passa tra i due stati. ScaleToBounds misura innanzitutto il layout secondario con i vincoli lookahead (o target). Poi, il layout stabile del componente secondario viene ridimensionato in modo da adattarsi ai limiti condivisi. ScaleToBounds può essere considerata una "scala grafica" tra gli stati.

Mentre RemeasureToBounds rimisura e riorganizza il layout secondario di sharedBounds con vincoli fissi animati in base alle dimensioni target. La nuova misurazione viene attivata dalla modifica delle dimensioni dei limiti, che potrebbe riguardare ogni frame.

Per i composabili Text, è consigliato ScaleToBounds in quanto evita il riadattamento e il riadattamento del testo su righe diverse. Per i limiti con proporzioni diverse e se vuoi una continuità fluida tra i due elementi condivisi, è consigliato RemeasureToBounds.

La differenza tra le due modalità di ridimensionamento è visibile negli esempi che seguono:

ScaleToBounds

RemeasureToBounds

Vai al layout finale

Per impostazione predefinita, durante la transizione tra due layout, le dimensioni del layout vengono animate tra lo stato iniziale e quello finale. Questo potrebbe essere un comportamento indesiderato quando si animano contenuti come il testo.

L'esempio seguente illustra il testo della descrizione "Lorem Ipsum" che entra nella schermata in due modi diversi. Nel primo esempio il testo viene adattato quando viene inserito man mano che le dimensioni del contenitore aumentano, mentre nel secondo esempio il testo non viene adattato quando aumenta. L'aggiunta di Modifier.skipToLookaheadSize() impedisce il riflusso man mano che cresce.

No Modifier.skipToLookahead(): nota che il testo "Lorem Ipsum" si ripete

Modifier.skipToLookahead(): nota che il testo "Lorem Ipsum" mantiene il suo stato finale all'inizio dell'animazione.

Clip e overlay

Un concetto importante quando si creano elementi condivisi in Compose è che, per poterli condividere tra composabili diversi, il rendering del composable viene elevato in un overlay di livello quando viene avviata la transizione alla sua corrispondenza nella destinazione. Il risultato è che uscirà dai limiti del livello principale e dalle relative trasformazioni (ad esempio alpha e scala).

Verrà visualizzato sopra altri elementi UI non condivisi. Al termine della transizione, l'elemento verrà eliminato dall'overlay al proprio DrawScope.

Per ritagliare un elemento condiviso in una forma, utilizza la funzione Modifier.clip() standard. Inseriscila dopo 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
)

Se devi assicurarti che un elemento condiviso non venga mai visualizzato all'esterno di un contenitore principale, puoi impostare clipInOverlayDuringTransition su sharedElement(). Per impostazione predefinita, per gli intervalli condivisi nidificati, clipInOverlayDuringTransition utilizza il percorso del clip dall'elemento sharedBounds() principale.

Per mantenere sempre in primo piano elementi dell'interfaccia utente specifici, come una barra in basso o un pulsante di azione flottante, durante la transizione di un elemento condiviso, utilizza Modifier.renderInSharedTransitionScopeOverlay(). Per impostazione predefinita, questo modificatore conserva i contenuti dell'overlay durante il periodo in cui è attiva la transizione condivisa.

Ad esempio, in Jetsnack, BottomAppBar deve essere posizionato sopra l'elemento condiviso finché la schermata non è visibile. L'aggiunta del modificatore al composable lo mantiene in primo piano.

Senza Modifier.renderInSharedTransitionScopeOverlay()

Con Modifier.renderInSharedTransitionScopeOverlay()

A volte potresti volere che il componibile non condiviso si anima e che resti sopra gli altri componibili prima della transizione. In questi casi, utilizza renderInSharedTransitionScopeOverlay().animateEnterExit() per animare il composabile in uscita durante l'esecuzione della transizione dell'elemento condiviso:

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

Figura 2. Barra app in basso che si apre e si chiude durante le transizioni dell'animazione

Nei rari casi in cui non vuoi che l'elemento condiviso venga visualizzato in un overlay, puoi impostare renderInOverlayDuringTransition su sharedElement() su false.

Inviare una notifica ai layout correlati delle modifiche alle dimensioni degli elementi condivisi

Per impostazione predefinita, sharedBounds() e sharedElement() non informano il contenitore principale di eventuali modifiche alle dimensioni durante le transizioni del layout.

Per propagare le modifiche delle dimensioni al contenitore principale durante la transizione, modifica il parametro placeHolderSize in PlaceHolderSize.animatedSize. In questo modo, l'elemento aumenta o diminuisce di dimensioni. Tutti gli altri elementi del layout reagiscono alla modifica.

PlaceholderSize.contentSize (valore predefinito)

PlaceholderSize.animatedSize

(notare come gli altri elementi dell'elenco si spostano verso il basso in risposta all'aumento di un elemento)