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

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

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

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

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

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

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

  • Для линейных размеров, измеряемых в пикселях, не зависящих от плотности экрана, используйте DimensionBuilders.DpProp.
  • Для угловых размеров, измеряемых в градусах, используйте DimensionBuilders.DegreesProp.
  • Для строковых значений используйте TypeBuilders.StringProp.
  • Для значений цвета используйте ColorBuilders.ColorProp.
  • Для значений с плавающей запятой используйте TypeBuilders.FloatProp.

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

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

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(),
                        TypeBuilders.StringLayoutConstraint.Builder("000").build(),
                    )
                        .build()
                )
            )
            .build()
    )

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

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

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

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

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

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

    companion object {
        val KEY_WATER_INTAKE = intAppDataKey("key_water_intake")
        val KEY_NOTE = stringAppDataKey("key_note")
    }

  2. В реализации onTileRequest() вызовите setState() и установите начальные сопоставления каждого ключа с определенным динамическим значением данных:

    override fun onTileRequest(
        requestParams: RequestBuilders.TileRequest
    ): ListenableFuture<Tile?> {
        // If the tile hasn't had any state set yet, use the default values
        val state =
            if (requestParams.currentState.keyToValueMapping.isNotEmpty())
                requestParams.currentState
            else
                StateBuilders.State.Builder()
                    .setStateMap(
                        dynamicDataMapOf(
                            KEY_WATER_INTAKE mapTo 200,
                            KEY_NOTE mapTo "Good"
                        )
                    )
                    .build()
    
        return Futures.immediateFuture(
            Tile.Builder()
                // Set resources, timeline, and other tile properties.
                .setState(state)
                .build()
        )
    }

  3. При создании макета в том месте, где вы хотите показывать данные из состояния, используйте объект типа Dynamic*. Вы также можете вызвать метод animate(), чтобы показать анимацию перехода от предыдущего значения к текущему:

    val waterIntakeValue =
        DynamicBuilders.DynamicInt32.from(KEY_WATER_INTAKE)

  4. При необходимости вы также можете обновить состояние, указав новые значения. Это может быть частью LoadAction плитки.

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

    val loadAction =
        loadAction(
            dynamicDataMapOf(
                KEY_WATER_INTAKE mapTo 400,
                KEY_NOTE mapTo "Outstanding"
            )
        )