Плиточная анимация

Плитки поддерживают несколько различных подходов к анимации, включая следующие:

Показать переход развертки

Чтобы продемонстрировать плавный переход от одного значения к другому, можно включить анимацию переходов для элемента, как показано в следующем фрагменте кода:

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()
    )
}

Установить направление дуги

Если ваша плитка содержит дугу, вы можете не захотеть, чтобы линия дуги или текст всегда расширялись в направлении, заданном по умолчанию для выбранного пользователем языка. Чтобы задать направление расширения дуги, используйте API 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()
    )
}

Показывать плавное затухание или скольжение

Чтобы более четко обозначить появление или исчезновение элемента на плитке или более тонко продемонстрировать пошаговое изменение значения плитки, используйте эффекты затухания и скольжения в анимации плитки.

Если макет плитки содержит элемент, значение которого изменяется, плитка показывает анимацию выхода элемента, затем обновляет макет и показывает анимацию входа элемента.

Плавные переходы

В следующем фрагменте кода показано, как реализовать плавные переходы с нарастанием и затуханием с помощью вспомогательных методов из DefaultContentTransitions . Чтобы определить пользовательские объекты FadeInTransition и FadeOutTransition , вызовите методы setFadeIn() и setFadeOut() соответственно в методах установки перехода.

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()
    )
}

Переходы слайдов

Этот другой фрагмент кода демонстрирует, как реализовать переходы «вход» и «выход» с помощью вспомогательных методов из DefaultContentTransitions . Вы также можете определить собственные объекты SlideInTransition и SlideOutTransition , вызвав setSlideIn() и setSlideOut() соответственно в методах установки перехода.

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()
    )
}

Показать трансформацию

Чтобы привлечь внимание к определенному элементу или области на плитке, к нему можно применить несколько типов преобразований, включая: поворот, масштабирование и перемещение.

Многие значения с плавающей точкой, связанные с преобразованиями, принимают динамические выражения , которые позволяют анимировать эти преобразования.

Вращение

Чтобы выполнить поворот по часовой стрелке вокруг настраиваемой точки поворота, используйте код, аналогичный следующему:

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()
)

Масштабирование

Чтобы увеличить или уменьшить элемент с помощью коэффициентов горизонтального и вертикального масштабирования, используйте код, аналогичный следующему:

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()
)

Геометрический перевод

Чтобы переместить элемент на определенное число пикселей плотности (dp) по экрану по горизонтали или вертикали, используйте код, подобный следующему:

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()
)

Анимации Лотти

Tiles поддерживает воспроизведение анимаций Lottie , используя синтаксис, аналогичный синтаксису изображений:

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()
        )
    }
}

Несколько моментов, на которые следует обратить внимание:

  • Поддерживается только часть файлов Lottie. Проверьте совместимость с помощью одного из следующих валидаторов:
    • Онлайн-валидатор: https://skottie.skia.org/ . В разделе «Отчёт о совместимости» файл должен пройти тесты «Ошибки спецификации», «Предупреждения спецификации» (с игнорированием общих свойств) и «Ошибки профиля низкого энергопотребления».
    • Библиотека проверки Rust: https://github.com/google/lottie-tools .
  • Воспроизведение Lottie поддерживается рендерерами тайлов с основной версией не ниже 1 и дополнительной версией не ниже 500 Если анимация не поддерживается, она не отображается, но остальная часть тайла отображается корректно. При необходимости можно предоставить резервный вариант , например, статическое изображение.

Не показывайте важную информацию в середине анимации.

Анимация отключается в нескольких ситуациях:

  • Системный рендеринг плиток может отключить анимацию для всех плиток.
  • Плитка может анимировать только 4 элемента одновременно. Если вы попытаетесь анимировать более 4 элементов одновременно, не все из них будут анимированы.

Если анимация отключена, элементы статичны и отображают конечное значение анимации. Поэтому не полагайтесь на поведение анимации, например на её продолжительность, для отображения важной информации.