Personalizza la transizione degli elementi condivisi

Per personalizzare la modalità di esecuzione dell'animazione di transizione degli elementi condivisi, sono disponibili alcuni parametri che possono essere utilizzati per modificare la transizione degli elementi condivisi.

Specifiche animazione

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

Ad esempio, per fare in modo che il testo nell'esempio precedente si sposti con un movimento 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 parametri boundsTransform

Modalità di ridimensionamento

Quando crei animazioni 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). A questo punto, il layout stabile del file secondario viene ridimensionato per adattarsi ai limiti condivisi. ScaleToBounds può essere considerato una "scala grafica" tra gli stati.

RemeasureToBounds rimisura e modifica il layout del 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 gli elementi componibili Text è consigliato ScaleToBounds perché eviterà il relayout e l'adattamento dinamico del contenuto del testo su righe diverse. Per limiti con proporzioni diverse e se desideri una continuità fluida tra i due elementi condivisi, si consiglia RemeasureToBounds.

La differenza tra le due modalità di ridimensionamento si può vedere negli esempi che seguono:

ScaleToBounds

RemeasureToBounds

Vai al layout finale

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

L'esempio seguente illustra il testo della descrizione "Lorem Ipsum" che entra nella schermata in due modi diversi. Nel primo esempio il testo si adatta automaticamente man mano che entra a mano a mano che il contenitore aumenta le dimensioni, nel secondo il testo non si adatta in base alle dimensioni del contenitore. L'aggiunta di Modifier.skipToLookaheadSize() impedisce la ripetizione del flusso 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 durante la creazione di elementi condivisi in Compose è che, per consentire la condivisione tra diversi elementi componibili, il rendering dell'elemento componibile viene elevato in un overlay di livelli quando la transizione inizia e corrisponde alla destinazione. L'effetto è che esce dai limiti del livello padre e dalle trasformazioni dei livelli (ad esempio alfa 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. Posizionalo dopo il 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 i limiti condivisi nidificati, clipInOverlayDuringTransition utilizza il percorso di clip dall'elemento principale sharedBounds().

Per mantenere sempre in alto elementi specifici dell'interfaccia utente, ad esempio una barra inferiore o un pulsante di azione mobile, 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 fino a quando lo schermo non sarà visibile. L'aggiunta del modificatore al componibile mantiene elevato il suo valore.

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 l'elemento 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 inferiore dell'app scorre in entrata e in uscita durante le transizioni dell'animazione

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

Invia una notifica ai layout di pari livello delle modifiche alle dimensioni degli elementi condivisi

Per impostazione predefinita, sharedBounds() e sharedElement() non inviano al contenitore principale notifiche relative a modifiche delle 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 si espande o si riduce. Tutti gli altri elementi nel layout rispondono al cambiamento.

PlaceholderSize.contentSize (valore predefinito)

PlaceholderSize.animatedSize

(Osserva come le altre voci nell'elenco si spostano verso il basso in risposta all'aumento di una voce)