Personaliza la transición de elementos compartidos

Para personalizar la forma en que se ejecuta la animación de transición de elementos compartidos, hay algunos parámetros que se pueden usar para cambiar la forma en que se transfieren los elementos compartidos.

Especificaciones de animación

Para cambiar las especificaciones de la animación utilizadas para el tamaño y el movimiento de posición, puedes especifica un parámetro boundsTransform diferente en Modifier.sharedElement(). Esto proporciona la posición inicial de Rect y la posición de destino de Rect.

Por ejemplo, para hacer que el texto del ejemplo anterior se mueva con un arco movimiento, especifica el parámetro boundsTransform para usar una especificación de 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
    )
)

Puedes usar cualquier AnimationSpec. En este ejemplo, se usa una especificación keyframes.

Figura 1: Ejemplo que muestra diferentes parámetros boundsTransform

Modo de cambio de tamaño

Cuando realizas animaciones entre dos límites compartidos, puedes establecer el parámetro resizeMode a RemeasureToBounds o ScaleToBounds. Este parámetro determina cómo el elemento compartido realiza una transición entre los dos estados. ScaleToBounds primero mide el diseño secundario con las restricciones de visualización anticipada (o objetivo). Luego, el diseño estable del elemento secundario se ajusta para que se ajuste a los límites compartidos. Se puede considerar ScaleToBounds como una "escala gráfica" entre los estados.

Mientras que RemeasureToBounds vuelve a medir y a diseñar el diseño secundario de sharedBounds con restricciones fijas animadas según el tamaño objetivo. La re-medición se activa por el cambio de tamaño de los límites, que podría ser cada fotograma.

Para los elementos componibles Text, se recomienda usar ScaleToBounds, ya que evitará el rediseño. y reprocesamiento del texto en diferentes líneas. Para límites que tienen diferentes relaciones de aspecto y si deseas una continuidad fluida entre los dos elementos compartidos, se recomienda RemeasureToBounds.

La diferencia entre los dos modos de cambio de tamaño se puede ver en los siguientes ejemplos:

ScaleToBounds

RemeasureToBounds

Ir al diseño final

De forma predeterminada, al hacer la transición entre dos diseños, se anima el tamaño del diseño. entre su estado inicial y final. Este puede ser un comportamiento no deseado cuando se anima contenido, como texto.

En el siguiente ejemplo, se ilustra el texto de descripción "Lorem Ipsum" que ingresa a la pantalla de dos maneras diferentes. En el primer ejemplo, el texto se reprocesa a medida que ingresa a medida que el tamaño del contenedor aumenta; en el segundo ejemplo, el texto no se reprocesa a medida que crece. Agregar Modifier.skipToLookaheadSize() evita el reprocesamiento a medida que crece.

No hay Modifier.skipToLookahead(). Observa el "Lorem Ipsum". reprocesamiento de texto

Modifier.skipToLookahead(): Observa que el texto "Lorem Ipsum" mantiene su estado final al comienzo de la animación.

Recortar y superposiciones

Un concepto importante a la hora de crear elementos compartidos en Compose es que, en orden para compartir entre diferentes elementos componibles, la renderización de componible se eleva a una superposición de capas cuando se inicia la transición a que coincida con el destino. El efecto de esto es que se escapará el límites del elemento superior y las transformaciones de sus capas (por ejemplo, el valor alfa y la escala).

Se renderizará sobre otros elementos de la IU no compartidos, una vez que se realice la transición Cuando finalice, se descartará el elemento de la superposición a su propio DrawScope.

Para recortar un elemento compartido a una forma, usa el Modifier.clip() estándar . Colócalo después de 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
)

Si necesitas asegurarte de que un elemento compartido nunca se renderice fuera de un elemento superior puedes configurar clipInOverlayDuringTransition en sharedElement(). De forma predeterminada, para los límites compartidos anidados, clipInOverlayDuringTransition usa la ruta de acceso del clip del sharedBounds() superior.

Para admitir la conservación de elementos específicos de la IU, como una barra inferior o una acción flotante siempre en la parte superior durante una transición de elementos compartidos, usa Modifier.renderInSharedTransitionScopeOverlay() De forma predeterminada, este mantiene el contenido en la superposición durante el tiempo en que se comparte cuando la transición está activa.

Por ejemplo, en Jetsnack, el BottomAppBar se debe colocar sobre el elemento compartido hasta que la pantalla no sea visible. Agrega el modificador en el elemento componible lo mantiene elevado.

Sin Modifier.renderInSharedTransitionScopeOverlay()

Con Modifier.renderInSharedTransitionScopeOverlay()

En ocasiones, tal vez quieras animar tu elemento no compartido componible permanecerá por encima de los otros elementos componibles antes de la transición. En esos casos, utiliza renderInSharedTransitionScopeOverlay().animateEnterExit() para animar el componible mientras se ejecuta la transición de elementos compartidos:

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

Figura 2: La barra inferior de la app se desliza hacia adentro y hacia afuera a medida que se produce la transición de la animación

En el caso poco frecuente de que quieras que tu elemento compartido no se renderice en una superposición, puedes establecer el renderInOverlayDuringTransition en sharedElement() como falso.

Notificar a los diseños del mismo nivel sobre los cambios en el tamaño de los elementos compartidos

De forma predeterminada, sharedBounds() y sharedElement() no notifican a la madre o el padre de cualquier tamaño cambia a medida que pasa el diseño.

Para propagar los cambios de tamaño al contenedor superior a medida que pasa cambia el parámetro placeHolderSize a PlaceHolderSize.animatedSize. Hacer y hace que el elemento aumente o se contraiga. Todos los demás elementos del diseño responden al cambio.

PlaceholderSize.contentSize (predeterminado)

PlaceholderSize.animatedSize

(Observa cómo los otros elementos de la lista se mueven hacia abajo en respuesta al crecimiento de un elemento)