Para personalizar a execução da animação de transição de elemento compartilhado, alguns parâmetros podem ser usados para mudar a transição dos elementos compartilhados.
Especificação da animação
Para mudar a especificação da animação usada para o movimento de tamanho e posição, especifique um parâmetro boundsTransform diferente em Modifier.sharedElement().
Isso fornece a posição Rect inicial e a posição Rect de destino.
Por exemplo, para fazer com que o texto no exemplo anterior se mova com um movimento de arco, especifique o parâmetro boundsTransform para usar uma especificação 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 ) )
Você pode usar qualquer AnimationSpec. Este exemplo usa uma especificação keyframes.
boundsTransformModo de redimensionamento
Ao animar entre dois limites compartilhados, você pode definir o parâmetro resizeMode como RemeasureToBounds ou ScaleToBounds. Esse parâmetro determina como o elemento compartilhado faz a transição entre os dois estados. ScaleToBounds primeiro mede o layout filho com as restrições de lookahead (ou de destino). Em seguida, o layout estável da criança é dimensionado para caber nos limites compartilhados.
ScaleToBounds pode ser considerado uma "escala gráfica" entre os estados.
Por outro lado, RemeasureToBounds mede e refaz o layout filho de sharedBounds com restrições fixas animadas com base no tamanho de destino. A nova medição é acionada pela mudança de tamanho dos limites, que pode ocorrer a cada frame.
Para elementos combináveis Text, recomendamos ScaleToBounds, porque ele evita o relayout e o reflow do texto em linhas diferentes. RemeasureToBounds é recomendado para limites que têm proporções diferentes e se você quiser uma continuidade fluida entre os dois elementos compartilhados.
A diferença entre os dois modos de redimensionamento pode ser vista nos exemplos a seguir:
|
|
|---|---|
Ativar e desativar elementos compartilhados de forma dinâmica
Por padrão, sharedElement() e sharedBounds() são configurados para animar as mudanças de layout sempre que uma chave correspondente é encontrada no estado de destino. No entanto, talvez você queira desativar essa animação de forma dinâmica com base em condições específicas, como a direção da navegação ou o estado atual da interface.
Para controlar se a transição de elemento compartilhado ocorre, personalize o SharedContentConfig transmitido para rememberSharedContentState(). A propriedade isEnabled determina se o elemento compartilhado está ativo.
O exemplo a seguir demonstra como definir uma configuração que só ativa a transição compartilhada ao navegar entre telas específicas (por exemplo, apenas de A para B), desativando-a para outras.
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 } } } } }
Por padrão, se um elemento compartilhado for desativado durante uma animação em andamento, ele ainda concluirá a animação atual em andamento para evitar a remoção acidental de animações em trânsito. Se você precisar remover o elemento enquanto a animação estiver em andamento, substitua shouldKeepEnabledForOngoingAnimation na interface SharedContentConfig para retornar "false".
Pular para o layout final
Por padrão, ao fazer a transição entre dois layouts, o tamanho do layout é animado entre o estado inicial e o final. Esse comportamento pode ser indesejável ao animar conteúdo como texto.
O exemplo a seguir ilustra o texto de descrição "Lorem Ipsum" entrando na tela de duas maneiras diferentes. No primeiro exemplo, o texto é refluído à medida que entra, à medida que o contêiner aumenta de tamanho. No segundo exemplo, o texto não é refluído à medida que cresce. A adição de Modifier.skipToLookaheadSize() impede o reflow à medida que ele cresce.
Sem |
|
|---|---|
Recorte e sobreposições
Para que os elementos compartilhados sejam compartilhados entre diferentes elementos combináveis, a renderização do elemento combinável é elevada em uma sobreposição de camada quando a transição é iniciada para a correspondência no destino. O efeito disso é que ele vai escapar dos limites do pai e das transformações de camada (por exemplo, alfa e escala).
Ele será renderizado sobre outros elementos de interface não compartilhados. Quando a transição terminar, o elemento será descartado da sobreposição para o próprio DrawScope.
Para recortar um elemento compartilhado em uma forma, use a função Modifier.clip() padrão. Coloque-o após o 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 você precisar garantir que um elemento compartilhado nunca seja renderizado fora de um contêiner pai, defina clipInOverlayDuringTransition em sharedElement(). Por padrão, para limites compartilhados aninhados, clipInOverlayDuringTransition usa o caminho de recorte do sharedBounds() pai.
Para manter elementos de interface específicos, como uma barra inferior ou um botão de ação
flutuante, sempre na parte de cima durante uma transição de elemento compartilhado, use
Modifier.renderInSharedTransitionScopeOverlay(). Por padrão, esse modificador mantém o conteúdo na sobreposição durante o período em que a transição compartilhada está ativa.
Por exemplo, no Jetsnack, a BottomAppBar precisa ser colocada na parte de cima do elemento compartilhado até que a tela não esteja visível. A adição do modificador ao elemento combinável o mantém elevado.
Sem |
Com |
|---|---|
Talvez você queira que o elemento combinável não compartilhado seja animado e permaneça na parte de cima dos outros elementos combináveis antes da transição. Nesses casos, use renderInSharedTransitionScopeOverlay().animateEnterExit() para animar o elemento combinável à medida que a transição de elemento compartilhado é executada:
JetsnackBottomBar( modifier = Modifier .renderInSharedTransitionScopeOverlay( zIndexInOverlay = 1f, ) .animateEnterExit( enter = fadeIn() + slideInVertically { it }, exit = fadeOut() + slideOutVertically { it } ) )
No caso raro de você querer que o elemento compartilhado não seja renderizado em uma sobreposição, defina renderInOverlayDuringTransition em sharedElement() como "false".
Notificar layouts irmãos sobre mudanças no tamanho do elemento compartilhado
Por padrão, sharedBounds() e sharedElement() não notificam o contêiner pai sobre mudanças de tamanho à medida que o layout faz a transição.
Para propagar mudanças de tamanho para o contêiner pai à medida que ele faz a transição, mude o parâmetro placeholderSize para PlaceholderSize.AnimatedSize. Isso faz com que o item cresça ou diminua. Todos os outros itens no layout respondem à mudança.
|
(Observe como os outros itens na lista se movem para baixo em resposta ao crescimento de um item) |
|---|---|