Personalizza la transizione degli elementi condivisi

Per personalizzare la modalità di esecuzione dell'animazione di transizione degli elementi condivisi, esistono alcuni parametri che possono essere utilizzati per modificare il modo in cui gli elementi condivisi vengono trasformati.

Specifiche animazione

Per modificare la specifica dell'animazione utilizzata per il movimento di dimensione e posizione, puoi specificare un parametro boundsTransform diverso su Modifier.sharedElement(). Fornisce la posizione Rect iniziale e la posizione Rect target.

Ad esempio, per spostare il testo nell'esempio precedente con un' animazione 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. In questo esempio viene utilizzata una specifica keyframes.

Figura 1. Esempio di diversi parametri boundsTransform

Modalità Ridimensiona

Quando esegui l'animazione tra due limiti condivisi, puoi impostare il parametro resizeMode su RemeasureToBounds o ScaleToBounds. Questo parametro determina la modalità di transizione dell'elemento condiviso tra i due stati. ScaleToBounds misura innanzitutto il layout secondario con i vincoli di lookahead (o target). Successivamente, il layout stabile del publisher secondario viene ridimensionato per rientrare nei limiti condivisi. ScaleToBounds può essere considerata come una "scala grafica" tra gli stati.

Mentre RemeasureToBounds rimisura e riconfigura il layout secondario di sharedBounds con vincoli fissi animati basati sulle dimensioni di destinazione. La nuova misurazione viene attivata dalla modifica delle dimensioni dei limiti, che potrebbe interessare ogni frame.

Per gli elementi componibili Text, si consiglia l'uso di ScaleToBounds perché eviterà il relayout e l'adattamento dinamico del testo su righe diverse. Per i limiti che hanno proporzioni diverse e se vuoi una continuità fluida tra i due elementi condivisi, ti consigliamo di utilizzare RemeasureToBounds.

La differenza tra le due modalità di ridimensionamento è evidente negli esempi riportati di seguito:

ScaleToBounds

RemeasureToBounds

Vai al layout finale

Per impostazione predefinita, quando passi da un layout all'altro, la dimensione del layout si anima tra lo stato iniziale e quello finale. Questo può essere un comportamento indesiderato durante l'animazione di contenuti come il testo.

L'esempio seguente illustra il testo descrittivo "Lorem Ipsum" che entra nella schermata in due modi diversi. Nel primo esempio, il testo viene adattato man mano che viene inserito man mano che il container aumenta, mentre nel secondo esempio il testo non viene adattato man mano che aumenta. L'aggiunta di Modifier.skipToLookaheadSize() impedisce l'adattamento dinamico del contenuto man mano che cresce.

No Modifier.skipToLookahead() - nota la ripetizione del flusso del testo "Lorem Ipsum"

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

Clip e overlay

Un concetto importante durante la creazione di elementi condivisi in Compose è che, affinché possano essere condivisi tra diversi componibili, il rendering dell'elemento componibile viene elevato in un overlay del livello quando viene avviata la transizione in modo che corrisponda alla destinazione. Il risultato è l'uscita dai limiti del padre e dalle trasformazioni del livello (ad esempio alfa e scala).

Verrà visualizzato sopra altri elementi dell'interfaccia utente non condivisi. Al termine della transizione, l'elemento verrà rilasciato dall'overlay al proprio DrawScope.

Per ritagliare un elemento condiviso in una forma, utilizza la funzione Modifier.clip() standard. Posizionalo 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 eseguito al di fuori di un contenitore principale, puoi impostare clipInOverlayDuringTransition su sharedElement(). Per impostazione predefinita, per i limiti condivisi nidificati, clipInOverlayDuringTransition utilizza il percorso del clip dell'elemento sharedBounds() principale.

Per supportare la conservazione di elementi specifici dell'interfaccia utente, come una barra inferiore o un pulsante di azione mobile, sempre in primo piano durante una transizione di elementi condivisi, utilizza Modifier.renderInSharedTransitionScopeOverlay(). Per impostazione predefinita, questo modificatore mantiene i contenuti nell'overlay durante il tempo in cui è attiva la transizione condivisa.

Ad esempio, in Jetsnack, BottomAppBar deve essere posizionato sopra l'elemento condiviso finché lo schermo non è visibile. L'aggiunta del modificatore al componibile lo mantiene elevato.

Senza Modifier.renderInSharedTransitionScopeOverlay()

Con Modifier.renderInSharedTransitionScopeOverlay()

A volte potresti voler creare un componibile non condiviso che si anima e rimanga sopra gli altri componibili prima della transizione. In questi casi, utilizza renderInSharedTransitionScopeOverlay().animateEnterExit() per animare il componibile 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.La barra dell'app inferiore che scorre dentro e fuori come transizioni dell'animazione

Nel raro caso in cui tu non voglia che l'elemento condiviso venga visualizzato in un overlay, puoi impostare renderInOverlayDuringTransition su sharedElement() su false.

Invia una notifica ai layout di pari livello delle modifiche alle dimensioni dell'elemento condiviso

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

Per propagare le modifiche alle dimensioni al contenitore principale durante la transizione, modifica il parametro placeHolderSize in PlaceHolderSize.animatedSize. In questo modo, l'elemento si ingrandisce o si riduce. Tutti gli altri elementi del layout rispondono al cambiamento.

PlaceholderSize.contentSize (valore predefinito)

PlaceholderSize.animatedSize

(nota come le altre voci nell'elenco vengono spostate verso il basso in risposta alla crescita di un elemento).