Animaciones de tarjetas

Las tarjetas admiten varios enfoques de animación, incluidos los siguientes:

Cómo mostrar una transición de barrido

Para mostrar un barrido suave de un valor a otro, puedes habilitar las animaciones de interpolación de un elemento, como se muestra en el siguiente fragmento de código:

private var startValue = 15f
private var endValue = 105f
private val animationDurationInMillis = 2000L // 2 seconds

override fun onTileRequest(requestParams: RequestBuilders.TileRequest): ListenableFuture<Tile> {
    val circularProgressIndicator =
        CircularProgressIndicator.Builder()
            .setProgress(
                FloatProp.Builder(/* static value */ 0.25f)
                    .setDynamicValue(
                        // Or you can use some other dynamic object, for example
                        // from the platform and then at the end of expression
                        // add animate().
                        DynamicFloat.animate(
                            startValue,
                            endValue,
                            AnimationSpec.Builder()
                                .setAnimationParameters(
                                    AnimationParameters.Builder()
                                        .setDurationMillis(animationDurationInMillis)
                                        .build()
                                )
                                .build(),
                        )
                    )
                    .build()
            )
            .build()

    return Futures.immediateFuture(
        Tile.Builder()
            .setResourcesVersion(RESOURCES_VERSION)
            .setTileTimeline(Timeline.fromLayoutElement(circularProgressIndicator))
            .build()
    )
}

Cómo establecer la dirección del arco

Si tu tarjeta contiene un arco, es posible que no quieras que la línea o el texto del arco siempre crezcan en la dirección de texto predeterminada para el idioma elegido por el usuario. Para especificar una dirección de crecimiento del arco, usa las APIs de ArcDirection:

public override fun onTileRequest(
    requestParams: RequestBuilders.TileRequest
): ListenableFuture<Tile> {
    return Futures.immediateFuture(
        Tile.Builder()
            .setResourcesVersion(RESOURCES_VERSION)
            .setTileTimeline(
                Timeline.fromLayoutElement(
                    EdgeContentLayout.Builder(deviceParameters)
                        .setResponsiveContentInsetEnabled(true)
                        .setEdgeContent(
                            Arc.Builder()
                                // Arc should always grow clockwise.
                                .setArcDirection(LayoutElementBuilders.ARC_DIRECTION_CLOCKWISE)
                                .addContent(
                                    ArcLine.Builder()
                                        // Set color, length, thickness, and more.
                                        // Arc should always grow clockwise.
                                        .setArcDirection(
                                            LayoutElementBuilders.ARC_DIRECTION_CLOCKWISE
                                        )
                                        .build()
                                )
                                .build()
                        )
                        .build()
                )
            )
            .build()
    )
}

Cómo mostrar una atenuación o deslizamiento suave

Para indicar con mayor claridad que un elemento aparece o desaparece en una tarjeta, o para mostrar de forma más sutil un cambio de paso en el valor de una tarjeta, usa efectos de atenuación y deslizamiento en tus animaciones de tarjetas.

Si un diseño de tarjeta contiene un elemento cuyo valor cambia, la tarjeta muestra la animación de salida del elemento y, luego, actualiza el diseño y muestra la animación de entrada del elemento.

Transiciones de atenuación

En el siguiente fragmento de código, se muestra cómo realizar transiciones de fundido de entrada y de salida con los métodos auxiliares de DefaultContentTransitions. Para definir objetos FadeInTransition y FadeOutTransition personalizados, llama a setFadeIn() y setFadeOut(), respectivamente, en la transición de los métodos set.

public override fun onTileRequest(
    requestParams: RequestBuilders.TileRequest
): ListenableFuture<Tile> {
    // Assumes that you've defined a custom helper method called
    // getTileTextToShow().
    val tileText = getTileTextToShow()
    return Futures.immediateFuture(
        Tile.Builder()
            .setResourcesVersion(RESOURCES_VERSION)
            .setTileTimeline(
                Timeline.fromLayoutElement(
                    Text.Builder(this, tileText)
                        .setModifiers(
                            Modifiers.Builder()
                                .setContentUpdateAnimation(
                                    AnimatedVisibility.Builder()
                                        .setEnterTransition(DefaultContentTransitions.fadeIn())
                                        .setExitTransition(DefaultContentTransitions.fadeOut())
                                        .build()
                                )
                                .build()
                        )
                        .build()
                )
            )
            .build()
    )
}

Transiciones de deslizamiento

En este otro fragmento de código, se muestra cómo realizar transiciones de deslizamiento hacia adentro y afuera con los métodos auxiliares de DefaultContentTransitions. También puedes definir objetos SlideInTransition y SlideOutTransition personalizados llamando a setSlideIn() y setSlideOut(), respectivamente, en los métodos set de transición.

public override fun onTileRequest(
    requestParams: RequestBuilders.TileRequest
): ListenableFuture<Tile> {
    // Assumes that you've defined a custom helper method called
    // getTileTextToShow().
    val tileText = getTileTextToShow()
    return Futures.immediateFuture(
        Tile.Builder()
            .setResourcesVersion(RESOURCES_VERSION)
            .setTileTimeline(
                Timeline.fromLayoutElement(
                    Text.Builder(this, tileText)
                        .setModifiers(
                            Modifiers.Builder()
                                .setContentUpdateAnimation(
                                    AnimatedVisibility.Builder()
                                        .setEnterTransition(
                                            DefaultContentTransitions.slideIn(
                                                ModifiersBuilders.SLIDE_DIRECTION_LEFT_TO_RIGHT
                                            )
                                        )
                                        .setExitTransition(
                                            DefaultContentTransitions.slideOut(
                                                ModifiersBuilders.SLIDE_DIRECTION_LEFT_TO_RIGHT
                                            )
                                        )
                                        .build()
                                )
                                .build()
                        )
                        .build()
                )
            )
            .build()
    )
}

Cómo mostrar una transformación

Para llamar la atención sobre un elemento o área específicos en una tarjeta, puedes aplicarle varios tipos de transformaciones, como rotación, ajuste de escala y traslación.

Muchos valores de punto flotante asociados con las transformaciones aceptan expresiones dinámicas, que te permiten animar estas transformaciones.

Rotación

Para realizar una rotación en el sentido de las agujas del reloj alrededor de un punto de pivote personalizable, usa un código similar al siguiente:

return Futures.immediateFuture(
    Tile.Builder()
        .setResourcesVersion(RESOURCES_VERSION)
        .setTileTimeline(
            Timeline.fromLayoutElement(
                Text.Builder(this, someTileText)
                    .setModifiers(
                        Modifiers.Builder()
                            .setTransformation(
                                ModifiersBuilders.Transformation.Builder()
                                    // Set the pivot point 50 dp from the left edge
                                    // and 100 dp from the top edge of the screen.
                                    .setPivotX(dp(50f))
                                    .setPivotY(dp(100f))
                                    // Rotate the element 45 degrees clockwise.
                                    .setRotation(degrees(45f))
                                    .build()
                            )
                            .build()
                    )
                    .build()
            )
        )
        .build()
)

Escalado

Para aumentar o reducir un elemento según factores de escala horizontales y verticales, usa un código similar al siguiente:

return Futures.immediateFuture(
    Tile.Builder()
        .setResourcesVersion(RESOURCES_VERSION)
        .setTileTimeline(
            Timeline.fromLayoutElement(
                Text.Builder(this, someTileText)
                    .setModifiers(
                        Modifiers.Builder()
                            .setTransformation(
                                ModifiersBuilders.Transformation.Builder()
                                    // Set the pivot point 50 dp from the left edge
                                    // and 100 dp from the top edge of the screen.
                                    .setPivotX(dp(50f))
                                    .setPivotY(dp(100f))
                                    // Shrink the element by a scale factor
                                    // of 0.5 horizontally and 0.75 vertically.
                                    .setScaleX(FloatProp.Builder(0.5f).build())
                                    .setScaleY(FloatProp.Builder(0.75f).build())
                                    .build()
                            )
                            .build()
                    )
                    .build()
            )
        )
        .build()
)

Traslación geométrica

Para mover un elemento una cantidad específica de píxeles independientes de la densidad (dp) de forma horizontal o vertical en la pantalla, usa un código similar al siguiente:

return Futures.immediateFuture(
    Tile.Builder()
        .setResourcesVersion(RESOURCES_VERSION)
        .setTileTimeline(
            Timeline.fromLayoutElement(
                Text.Builder(this, someTileText)
                    .setModifiers(
                        Modifiers.Builder()
                            .setTransformation(
                                ModifiersBuilders.Transformation.Builder()
                                    // Translate (move) the element 60 dp to the right
                                    // and 80 dp down.
                                    .setTranslationX(dp(60f))
                                    .setTranslationY(dp(80f))
                                    .build()
                            )
                            .build()
                    )
                    .build()
            )
        )
        .build()
)

Animaciones de Lottie

Las tarjetas admiten la reproducción de animaciones de Lottie con una sintaxis similar a la de las imágenes:

class LottieAnimation : TileService() {

    val lottieResourceId = "lottie_animation"

    override fun onTileRequest(requestParams: RequestBuilders.TileRequest): ListenableFuture<Tile> {

        val layout =
            LayoutElementBuilders.Image.Builder()
                .setWidth(dp(150f))
                .setHeight(dp(150f))
                .setResourceId(lottieResourceId)
                .build()

        return Futures.immediateFuture(
            Tile.Builder()
                .setResourcesVersion(RESOURCES_VERSION)
                .setTileTimeline(Timeline.fromLayoutElement(layout))
                .build()
        )
    }

    override fun onTileResourcesRequest(
        requestParams: ResourcesRequest
    ): ListenableFuture<Resources> {

        val lottieImage =
            ResourceBuilders.ImageResource.Builder()
                .setAndroidLottieResourceByResId(
                    ResourceBuilders.AndroidLottieResourceByResId.Builder(R.raw.lottie)
                        .setStartTrigger(createOnVisibleTrigger())
                        .build()
                )
                .build()

        return Futures.immediateFuture(
            Resources.Builder()
                .setVersion(requestParams.version)
                .addIdToImageMapping(lottieResourceId, lottieImage)
                .build()
        )
    }
}

Ten en cuenta lo siguiente:

  • Solo se admite un subconjunto de archivos Lottie. Comprueba la compatibilidad con uno de los siguientes validadores:
    • Un validador en línea: https://skottie.skia.org/. En la sección “Informe de compatibilidad”, el archivo debe superar las pruebas de “Errores de especificación”, “Advertencias de especificación” (sin tener en cuenta las propiedades comunes) y “Errores de perfil de potencia baja”.
    • Una biblioteca de validación de Rust: https://github.com/google/lottie-tools
  • Los renderizadores de mosaicos admiten la reproducción de Lottie con una versión principal de al menos 1 y una versión secundaria de al menos 500. Si no se admite una animación determinada, esta no aparecerá, pero el resto de la tarjeta se renderizará según lo previsto. Si es necesario, puedes proporcionar una opción de resguardo, como una imagen estática.

No muestres información importante en medio de una animación

Hay varias situaciones en las que se inhabilitan las animaciones:

  • La renderización de tarjetas del sistema puede inhabilitar las animaciones para todas las tarjetas.
  • Una tarjeta puede animar solo 4 elementos a la vez. Si intentas animar más de 4 elementos al mismo tiempo, no todos mostrarán una animación.

Cuando se inhabilita una animación, los elementos son estáticos y muestran el valor final de la animación. Por este motivo, no confíes en el comportamiento de la animación, como su duración, para mostrar información importante.