Per personalizzare l'esecuzione dell'animazione della transizione degli elementi condivisi, puoi utilizzare alcuni parametri per modificare la transizione degli elementi condivisi.
Specifiche dell'animazione
Per modificare la specifica dell'animazione utilizzata per il movimento di dimensioni e posizione, puoi specificare un parametro boundsTransform diverso su Modifier.sharedElement().
In questo modo, vengono fornite la posizione Rect iniziale e la posizione Rect target.
Ad esempio, per fare in modo che il testo nell'esempio precedente si muova con un movimento ad 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.
boundsTransform parametriModalità 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 da uno stato all'altro. ScaleToBounds misura prima il layout secondario con i vincoli di lookahead (o target). Poi, il layout stabile del componente secondario viene ridimensionato per adattarsi ai limiti condivisi.
ScaleToBounds può essere considerato una "scala grafica" tra gli stati.
Al contrario, RemeasureToBounds ricalcola le dimensioni e 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 potenzialmente avvenire a ogni frame.
Per i componibili Text, è consigliato ScaleToBounds, in quanto evita il ricalcolo del layout e il ritorno a capo del testo su righe diverse. RemeasureToBounds è consigliato per i limiti con proporzioni diverse e se vuoi una continuità fluida tra i due elementi condivisi.
La differenza tra le due modalità di ridimensionamento è visibile negli esempi seguenti:
|
|
|---|---|
Attiva e disattiva dinamicamente gli elementi condivisi
Per impostazione predefinita, sharedElement() e sharedBounds() sono configurati per animare le modifiche al layout ogni volta che viene trovata una chiave corrispondente nello stato di destinazione. Tuttavia, potresti voler disattivare questa animazione in modo dinamico in base a condizioni specifiche, come la direzione di navigazione o lo stato attuale della UI.
Per controllare se la transizione degli elementi condivisi avviene, puoi personalizzare l'oggetto SharedContentConfig passato a rememberSharedContentState(). La proprietà isEnabled determina se l'elemento condiviso è attivo.
L'esempio seguente mostra come definire una configurazione che attiva la transizione condivisa solo quando si naviga tra schermate specifiche (ad esempio, solo da A a B), disattivandola per le altre.
SharedTransitionLayout { val transition = updateTransition(currentState) transition.AnimatedContent { targetState -> // Create the configuration that depends on state changing. fun animationConfig() : SharedTransitionScope.SharedContentConfig { return object : SharedTransitionScope.SharedContentConfig { override val SharedTransitionScope.SharedContentState.isEnabled: Boolean // For this example, we only enable the transition in one direction // from A -> B and not the other way around. get() = transition.currentState == "A" && transition.targetState == "B" } } when (targetState) { "A" -> Box( modifier = Modifier .sharedElement( rememberSharedContentState( key = "shared_box", config = animationConfig() ), animatedVisibilityScope = this ) // ... ) { // Your content } "B" -> { Box( modifier = Modifier .sharedElement( rememberSharedContentState( key = "shared_box", config = animationConfig() ), animatedVisibilityScope = this ) // ... ) { // Your content } } } } }
Per impostazione predefinita, se un elemento condiviso viene disattivato durante un'animazione in corso, l'animazione corrente viene comunque completata per evitare di rimuovere accidentalmente le animazioni in corso. Se devi rimuovere l'elemento mentre l'animazione è in corso, puoi eseguire l'override di shouldKeepEnabledForOngoingAnimation nell'interfaccia SharedContentConfig per restituire false.
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 mostra il testo descrittivo "Lorem Ipsum" che entra
nello schermo in due modi diversi. Nel primo esempio, il testo viene ridisposto man mano che entra, mentre il contenitore aumenta di dimensioni. Nel secondo esempio, il testo non viene ridisposto man mano che cresce. L'aggiunta di Modifier.skipToLookaheadSize() impedisce il reflow man mano che cresce.
No |
|
|---|---|
Clip e overlay
Affinché gli elementi condivisi possano essere condivisi tra diversi componibili, il rendering del componibile viene elevato in un overlay di livello quando viene avviata la transizione alla corrispondenza nella destinazione. L'effetto è che uscirà dai limiti dell'elemento principale e dalle trasformazioni del livello (ad esempio, l'alpha e la scala).
Verrà visualizzato sopra altri elementi UI non condivisi. Una volta completata la transizione, l'elemento verrà rimosso dall'overlay e inserito nel proprio DrawScope.
Per ritagliare un elemento condiviso in una forma, utilizza la funzione standard Modifier.clip(). 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 visualizzato al di fuori di un contenitore principale, puoi impostare clipInOverlayDuringTransition su sharedElement(). Per impostazione predefinita, per i limiti condivisi nidificati, clipInOverlayDuringTransition utilizza il percorso di ritaglio dell'elemento principale sharedBounds().
Per mantenere sempre in primo piano elementi UI specifici, come una barra inferiore o un pulsante di azione floating, durante una transizione di elementi condivisi, utilizza Modifier.renderInSharedTransitionScopeOverlay(). Per impostazione predefinita, questo modificatore mantiene i contenuti nell'overlay durante il periodo in cui la transizione condivisa è attiva.
Ad esempio, in Jetsnack, il 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 |
Con |
|---|---|
Potresti voler applicare un'animazione anche al composable non condiviso, oltre a mantenerlo sopra gli altri composable prima della transizione. In questi casi, utilizza renderInSharedTransitionScopeOverlay().animateEnterExit() per animare l'uscita del composable durante l'esecuzione della transizione dell'elemento condiviso:
JetsnackBottomBar( modifier = Modifier .renderInSharedTransitionScopeOverlay( zIndexInOverlay = 1f, ) .animateEnterExit( enter = fadeIn() + slideInVertically { it }, exit = fadeOut() + slideOutVertically { it } ) )
Nel raro caso in cui tu non voglia che l'elemento condiviso venga visualizzato in un overlay, puoi impostare renderInOverlayDuringTransition su sharedElement() su false.
Notifica ai layout di pari livello le modifiche alle dimensioni degli elementi condivisi
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 rimpicciolisce. Tutti gli altri elementi del layout rispondono alla modifica.
|
(Nota come gli altri elementi dell'elenco si spostano verso il basso in risposta all'espansione dell'elemento) |
|---|---|