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

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

Показать плавный переход

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

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

override fun onTileRequest(requestParams: TileRequest) =
   
Futures.immediateFuture(
       
// Add timeline and layout containers. CircularProgressIndicator is an
       
// inner element of those containers.
       
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()
       
// Finish building all elements that contain CircularProgressIndicator.
   
)
private float defaultValue = 0f;
private float startValue = 15f;
private float endValue = 105f;
private float animationDurationInMillis = 2000f; // 2 seconds

@Override
protected ListenableFuture<Tile> onTileRequest(
       
@NonNull TileRequest requestParams
) {
   
return Futures.immediateFuture(
       
// Add timeline and layout containers. CircularProgressIndicator is an
       
// inner element of those containers.
       
new CircularProgressIndicator.Builder()
           
.setProgress(
               
new 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,
                           
new AnimationSpec.Builder()
                               
.setAnimationParameters(
                                   
new AnimationParameters.Builder()
                                       
.setDurationMillis(animationDurationInMillis)
                                       
.build()
                               
).build()
                       
)
                   
).build()
           
).build()
       
// Finish building all elements that contain CircularProgressIndicator.
   
);
}

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

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

@OptIn(ProtoLayoutExperimental::class)
public override fun onTileRequest(
        requestParams
: RequestBuilders.TileRequest
): ListenableFuture<Tile> {
   
return Futures.immediateFuture(Tile.Builder()
       
.setResourcesVersion(RESOURCES_VERSION)
       
.setTileTimeline(Timeline.fromLayoutElement(
           
EdgeContentLayout.Builder(deviceParameters)
               
.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()
   
)
}
@OptIn(markerClass = ProtoLayoutExperimental.class)
@NonNull
@Override
protected ListenableFuture<Tile> onTileRequest(
       
@NonNull RequestBuilders.TileRequest requestParams
) {
   
return Futures.immediateFuture(new Tile.Builder()
       
.setResourcesVersion(RESOURCES_VERSION)
       
.setTileTimeline(Timeline.fromLayoutElement(
           
new EdgeContentLayout.Builder(deviceParameters)
               
.setEdgeContent(
                   
new Arc.Builder()
                       
// Arc should always grow clockwise.
                       
.setArcDirection(LayoutElementBuilders.ARC_DIRECTION_CLOCKWISE)

                       
.addContent(
                           
new 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() соответственно в методах установки перехода.

@OptIn(ProtoLayoutExperimental::class)
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(
                   
ModifiersBuilders.Modifiers.Builder()
                       
.setContentUpdateAnimation(AnimatedVisibility.Builder()
                           
.setEnterTransition(
                                   
DefaultContentTransitions.fadeIn())
                           
.setExitTransition(
                                   
DefaultContentTransitions.fadeOut()
                           
).build())
               
).build())
       
).build()
   
)
}
@OptIn(markerClass = ProtoLayoutExperimental.class)
@NonNull
@Override
protected ListenableFuture<Tile> onTileRequest(
       
@NonNull RequestBuilders.TileRequest requestParams
) {
   
// Assumes that you've defined a custom helper method called
   
// getTileTextToShow().
   
String tileText = getTileTextToShow();

   
return Futures.immediateFuture(new Tile.Builder()
       
.setResourcesVersion(RESOURCES_VERSION)
       
.setTileTimeline(Timeline.fromLayoutElement(
           
new Text.Builder(this, tileText)
               
.setModifiers(
                   
new ModifiersBuilders.Modifiers.Builder()
                       
.setContentUpdateAnimation(new AnimatedVisibility.Builder()
                           
.setEnterTransition(
                                   
DefaultContentTransitions.fadeIn())
                           
.setExitTransition(
                                   
DefaultContentTransitions.fadeOut())
                           
.build())
               
.build()))
       
.build()
   
);
}

Переходы между слайдами

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

@OptIn(ProtoLayoutExperimental::class)
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(
                                    SLIDE
_DIRECTION_LEFT_TO_RIGHT)
                           
).setExitTransition(
                               
DefaultContentTransitions.slideOut(
                                    SLIDE
_DIRECTION_LEFT_TO_RIGHT)
                           
).build()
                       
).build()
               
).build()
       
)).build()
   
)
}
@OptIn(markerClass = ProtoLayoutExperimental.class)
@NonNull
@Override
protected ListenableFuture<Tile> onTileRequest(
       
@NonNull RequestBuilders.TileRequest requestParams
) {
   
// Assumes that you've defined a custom helper method called
   
// getTileTextToShow().
   
String tileText = getTileTextToShow();
   
return Futures.immediateFuture(Tile.Builder()
       
.setResourcesVersion(RESOURCES_VERSION)
       
.setTileTimeline(Timeline.fromLayoutElement(
           
new Text.Builder(this, tileText)
               
.setModifiers(
                   
new Modifiers.Builder()
                       
.setContentUpdateAnimation(
                           
new AnimatedVisibility.Builder()
                               
.setEnterTransition(
                                   
DefaultContentTransitions.slideIn(
                                        SLIDE_DIRECTION_LEFT_TO_RIGHT
))
                               
.setExitTransition(
                                   
DefaultContentTransitions.slideOut(
                                        SLIDE_DIRECTION_LEFT_TO_RIGHT
))
                               
.build())
                       
.build())
               
.build()))
       
.build()
   
);
}

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

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

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

Вращение

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

// Last line in your onTileRequest() method implementation.
return Futures.immediateFuture(Tile.Builder()
   
.setResourcesVersion(RESOURCES_VERSION)
   
.setTileTimeline(Timeline.fromLayoutElement(
       
Text.Builder(this, someTileText)
           
.setModifiers(
                   
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(50))
                       
.setPivotY(dp(100))
                       
// Rotate the element 45 degrees clockwise.
                       
.setRotation(
                            degrees
(45f)
                       
).build()

           
).build())
   
).build()
)
// Last line in your onTileRequest() method implementation.
return Futres.immediateFuture(new Tile.Builder()
   
.setResourcesVersion(RESOURCES_VERSION)
   
.setTileTimeline(Timeline.fromLayoutElement(
       
new Text.Builder(this, someTileText)
           
.setModifiers(
                   
new 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(50))
                       
.setPivotY(dp(100))
                       
// Rotate the element 45 degrees clockwise.
                       
.setRotation(
                            degrees
(45f))
                       
.build())

           
.build()))
   
.build()
);

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

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

// Last line in your onTileRequest() method implementation.
return Futures.immediateFuture(Tile.Builder()
   
.setResourcesVersion(RESOURCES_VERSION)
   
.setTileTimeline(Timeline.fromLayoutElement(
       
Text.Builder(this, someTileText)
           
.setModifiers(
                   
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(50))
                       
.setPivotY(dp(100))
                       
// Shrink the element by a scale factor
                       
// of 0.5 horizontally and 0.75 vertically.
                       
.setScaleX(TypeBuilders.FloatProp.Builder(0.5f)
                               
.build())
                       
.setScaleY(TypeBuilders.FloatProp.Builder(0.75f)
                               
.build()
                       
).build()

           
).build())
   
).build()
)
// Last line in your onTileRequest() method implementation.
return Futres.immediateFuture(new Tile.Builder()
   
.setResourcesVersion(RESOURCES_VERSION)
   
.setTileTimeline(Timeline.fromLayoutElement(
       
new Text.Builder(this, someTileText)
           
.setModifiers(
                   
new 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(50))
                       
.setPivotY(dp(100))
                       
// Shrink the element by a scale factor
                       
// of 0.5 horizontally and 0.75 vertically.
                       
.setScaleX(new TypeBuilders.FloatProp.Builder(0.5f)
                               
.build())
                       
.setScaleY(new TypeBuilders.FloatProp.Builder(0.75f)
                               
.build())
                       
.build())

           
.build()))
   
.build()
);

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

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

// Last line in your onTileRequest() method implementation.
return Futures.immediateFuture(Tile.Builder()
   
.setResourcesVersion(RESOURCES_VERSION)
   
.setTileTimeline(Timeline.fromLayoutElement(
       
Text.Builder(this, someTileText)
           
.setModifiers(
                   
ModifiersBuilders.Transformation.Builder()
                       
// Translate (move) the element 60 dp to the right
                       
// and 80 dp down.
                       
.setTranslationX(dp(60))
                       
.setTranslationY(dp(80))
                       
.build()

           
).build())
   
).build()
)
// Last line in your onTileRequest() method implementation.
return Futres.immediateFuture(new Tile.Builder()
   
.setResourcesVersion(RESOURCES_VERSION)
   
.setTileTimeline(Timeline.fromLayoutElement(
       
new Text.Builder(this, someTileText)
           
.setModifiers(
                   
new ModifiersBuilders.Transformation.Builder()
                       
// Translate (move) the element 60 dp to the right
                       
// and 80 dp down.
                       
.setTranslationX(dp(60))
                       
.setTranslationY(dp(80))
                       
.build())

           
.build()))
   
.build()
);

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

Есть несколько ситуаций, в которых анимация отключена:

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

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