Показывать динамические обновления в плитках

Начиная с версии Tiles 1.2, вы можете осуществлять потоковую передачу обновлений данных платформы с помощью динамических выражений . Затем вы можете связать эти обновления с анимацией на плитках. Ваше приложение получает обновления этого значения каждую секунду.

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

Связывание динамических выражений с источниками данных

Пространства имен androidx.wear.protolayout и androidx.wear.protolayout.material содержат множество классов, поля которых принимают динамические выражения. Несколько примеров включают следующее:

Чтобы использовать динамическое выражение в качестве возможного значения для элемента в плитке, используйте соответствующий тип динамического свойства *Prop элемента и передайте источник данных методу setDynamicValue() класса-построителя динамического типа свойства.

Плитки поддерживают следующие типы динамических свойств:

При использовании динамического выражения, влияющего на физические размеры (любое значение в плитке, кроме цвета), необходимо также указать набор связанных ограничений, например строковый формат. Эти ограничения позволяют средству визуализации системы определить максимальный объем пространства, которое значение может занимать внутри плитки. Обычно эти ограничения указываются на уровне элемента, а не на уровне динамического выражения, путем вызова метода, который начинается с setLayoutConstraintsForDynamic* .

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

Котлин

import androidx.wear.protolayout.material.Text

public override fun onTileRequest(requestParams: RequestBuilders.TileRequest) =
    Futures.immediateFuture(Tile.Builder()
        .setResourcesVersion(RESOURCES_VERSION)
        .setFreshnessIntervalMillis(60 * 60 * 1000) // 60 minutes
        .setTileTimeline(Timeline.fromLayoutElement(
            Text.Builder(this,
                TypeBuilders.StringProp.Builder("--")
                    .setDynamicValue(PlatformHealthSources.heartRateBpm()
                        .format()
                        .concat(DynamicBuilders.DynamicString.constant(" bpm")))
                    .build(),
                StringLayoutConstraint.Builder("000")
                    .build()
                ).build()
            )
        ).build()
    )

Ява

import androidx.wear.protolayout.material.Text;

@Override
protected ListenableFuture<Tile> onTileRequest(
       @NonNull TileRequest requestParams
) {
    return Futures.immediateFuture(new Tile.Builder()
        .setResourcesVersion(RESOURCES_VERSION)
        .setFreshnessIntervalMillis(60 * 60 * 1000) // 60 minutes
        .setTileTimeline(Timeline.fromLayoutElement(
            new Text.Builder(
                this,
                new TypeBuilders.StringProp.Builder("--")
                    .setDynamicValue(PlatformHealthSources.heartRateBpm()
                        .format()
                        .concat(DynamicBuilders.DynamicString.constant(" bpm")))
                    .build(),
                new StringLayoutConstraint.Builder("000")
                    .build()
                ).build())
        ).build()
    );
}

Используйте небольшое количество выражений в пределах одной плитки.

Wear OS накладывает ограничение на количество выражений, которые может иметь одна плитка. Если плитка содержит слишком много динамических выражений, динамические значения игнорируются, и система возвращается к статическим значениям, которые вы предоставляете соответствующим типам динамических свойств.

Вы можете смело добавить в плитку следующий набор выражений, так как всего выражений не так много. Следовательно, плитка ведет себя корректно:

Котлин

val personHealthInfo = DynamicString.constant("This person has walked ")
    .concat(PlatformHealthSources.dailySteps()
        .div(1000)
        .format())
    .concat("thousands of steps and has a current heart rate ")
    .concat(PlatformHealthSources.heartRateBpm()
        .format())
    .concat(" beats per minute")

Ява

DynamicString personHealthInfo =
    DynamicString.constant("This person has walked ")
        .concat(PlatformHealthSources.dailySteps()
            .div(1000)
            .format())
        .concat("thousands of steps and has a current heart rate ")
        .concat(PlatformHealthSources.heartRateBpm()
            .format())
        .concat(" beats per minute");

Однако на этой плитке может быть слишком много выражений:

Котлин

// Note that this template is applied as many times as the loop iterates.
// The system doesn't reuse dynamic expressions.
val dynamicStringTemplate = PlatformHealthSources.dailySteps()
    .div(1000)
    .format()

for (person in people) {
  // SomeProperty
    .setDynamicValue(
        DynamicBuilders.DynamicString.constant("Steps for ")
            .concat(person)
            .concat(" are ")
            .concat(dynamicStringTemplate)
    )
}

Ява

// Note that this template is applied as many times as the loop iterates.
// The system doesn't reuse dynamic expressions.
DynamicString dynamicStringTemplate =
    PlatformHealthSources.dailySteps()
        .div(1000)
        .format();

for (int i = 0; i < people.size(); i++) {
  // SomeProperty
    .setDynamicValue(
        DynamicBuilders.DynamicString.constant("Steps for ")
            .concat(people[i])
            .concat(" are ")
            .concat(dynamicStringTemplate)
    );
}

Консолидация динамических данных в объект состояния

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

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

  1. Установите набор ключей, которые представляют различные значения состояния вашей плитки. В этом примере создаются ключи для забора воды и примечание:

    Котлин

    companion object {
        val KEY_WATER_INTAKE = AppDataKey<DynamicInt32>("water_intake")
        val KEY_NOTE = AppDataKey<DynamicString>("note")
    }
    

    Ява

    private static final AppDataKey<DynamicInt32> KEY_WATER_INTAKE =
        new AppDataKey<DynamicInt32>("water_intake");
    private static final AppDataKey<DynamicString> KEY_NOTE =
        new AppDataKey<DynamicString>("note");
    
  2. В вашей реализации onTileRequest() вызовите setState() и установите начальные сопоставления каждого ключа с конкретным значением динамических данных:

    Котлин

    override fun onTileRequest(requestParams: TileRequest):
            ListenableFuture<Tile> {
        val state = State.Builder()
            .addKeyToValueMapping(KEY_WATER_INTAKE,
                DynamicDataBuilders.DynamicDataValue.fromInt(200))
            .addKeyToValueMapping(KEY_NOTE,
                DynamicDataBuilders.DynamicDataValue.fromString("Note about day"))
        .build()
        // ...
    
        return Futures.immediateFuture(Tile.Builder()
            // Set resources, timeline, and other tile properties.
            .setState(state)
            .build()
        )
    

    Ява

    @Override
    protected ListenableFuture<Tile> onTileRequest(
                ListenableFuture<Tile> {
        State state = new State.Builder()
            .addKeyToValueMapping(KEY_WATER_INTAKE,
                DynamicDataBuilders.DynamicDataValue.fromInt(200))
            .addKeyToValueMapping(KEY_NOTE,
                DynamicDataBuilders.DynamicDataValue.fromString("Note about day"))
        .build();
        // ...
    
        return Futures.immediateFuture(Tile.Builder()
            // Set resources, timeline, and other tile properties.
            .setState(state)
            .build()
        );
    }
    
  3. При создании макета в том месте, где вы хотите отобразить эти данные из состояния, используйте объект типа Dynamic* . Вы также можете вызвать animate() чтобы показать анимацию от предыдущего значения до текущего значения:

    Котлин

    DynamicInt32.from(KEY_WATER_INTAKE).animate()
    

    Ява

    DynamicInt32.from(KEY_WATER_INTAKE).animate();
    
  4. При необходимости вы также можете обновить состояние, добавив новые значения. Это может быть частью LoadAction плитки.

    В этом примере значение водозабора обновляется до 400 :

    Котлин

    state.addKeyToValueMapping(KEY_WATER_INTAKE,
            DynamicDataBuilders.DynamicDataValue.fromInt(400))
    

    Ява

    state.addKeyToValueMapping(KEY_WATER_INTAKE,
            DynamicDataBuilders.DynamicDataValue.fromInt(400));
    
{% дословно %} {% дословно %} {% дословно %} {% дословно %}