Od wersji 1.2 możesz przesyłać strumieniowo aktualizacje danych platformy za pomocą wyrażeń dynamicznych. Aktualizacje możesz następnie powiązać z animacjami w kafelkach. Aplikacja aktualizuje tę wartość co sekundę.
Korzystając z wyrażeń dynamicznych, nie musisz odświeżać całego kafelka po zmianie jego zawartości. Aby zwiększyć atrakcyjność kafelków, animuj te obiekty dynamiczne.
Powiązywanie wyrażeń dynamicznych ze źródłami danych
Przestrzenie nazw androidx.wear.protolayout
i androidx.wear.protolayout.material
zawierają wiele klas, których pola akceptują wyrażenia dynamiczne.
Oto kilka przykładów:
- Kilka wartości długości, w tym długość obiektu
Arc
i długość obiektuCircularProgressIndicator
. - Dowolny kolor, np. kolor treści obiektu
Button
. - Wiele wartości ciągów, w tym zawartość obiektu
Text
, zawartość obiektuLayoutElementsBuilders.Text
i opis treści obiektuCircularProgressIndicator
.
Aby użyć wyrażenia dynamicznego jako możliwej wartości elementu w kafelku, użyj odpowiedniego typu właściwości dynamicznej *Prop
tego elementu i przekaż źródło danych do metody setDynamicValue()
klasy konstruktora typu właściwości dynamicznej.
Kafelki obsługują te typy właściwości dynamicznych:
- W przypadku wymiarów liniowych wyrażonych w pikselach niezależnych od wyświetlacza użyj parametru
DimensionBuilders.DpProp
. - W przypadku wymiarów kątowych mierzonych w stopniach używaj funkcji
DimensionBuilders.DegreesProp
. - W przypadku wartości ciągu znaków użyj funkcji
TypeBuilders.StringProp
. - Jako wartości kolorów użyj
ColorBuilders.ColorProp
. - W przypadku wartości zmiennoprzecinkowych użyj funkcji
TypeBuilders.FloatProp
.
Gdy używasz wyrażenia dynamicznego, które ma wpływ na wymiary fizyczne – dowolną wartość w kafelku z wyjątkiem koloru – musisz też określić zestaw powiązanych ograniczeń, takich jak format ciągu znaków. Te ograniczenia pozwalają mechanizmowi renderowania systemowego określić maksymalną ilość miejsca, jaką dana wartość może zająć w kafelku. Zwykle te ograniczenia określa się na poziomie elementu, a nie na poziomie wyrażeń dynamicznych, wywołując metodę, która zaczyna się od setLayoutConstraintsForDynamic*
.
Poniższy fragment kodu pokazuje, jak wyświetlać aktualizacje tętna za pomocą 3 cyfr z wartością zastępczą --
:
Kotlin
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() )
Java
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() ); }
Używanie niewielkiej liczby wyrażeń w jednym kafelku
Wear OS nakłada limit na liczbę wyrażeń, które może zawierać pojedynczy kafelek. Jeśli kafelek zawiera łącznie zbyt wiele wyrażeń dynamicznych, wartości dynamiczne są ignorowane, a system wraca do wartości statycznych podanych przez Ciebie dla poszczególnych typów właściwości dynamicznych.
Do kafelka możesz bezpiecznie dodać poniższy zestaw wyrażeń, ponieważ łączna liczba wyrażeń nie jest zbyt duża. W związku z tym kafelek działa prawidłowo:
Kotlin
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")
Java
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");
Ten kafelek może jednak zawierać zbyt wiele wyrażeń:
Kotlin
// 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) ) }
Java
// 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) ); }
Konsolidacja danych dynamicznych w obiekcie stanu
Możesz skonsolidować najnowszy zestaw aktualizacji ze źródeł danych w stan, który przekazujesz do kafelka na potrzeby renderowania wartości.
Aby korzystać w swoich kafelkach z informacji o stanie, wykonaj te czynności:
Ustal zestaw kluczy reprezentujących różne wartości stanu kafelka. W tym przykładzie tworzymy klucze do ilości wody oraz notatkę:
Kotlin
companion object { val KEY_WATER_INTAKE = AppDataKey<DynamicInt32>("water_intake") val KEY_NOTE = AppDataKey<DynamicString>("note") }
Java
private static final AppDataKey<DynamicInt32> KEY_WATER_INTAKE = new AppDataKey<DynamicInt32>("water_intake"); private static final AppDataKey<DynamicString> KEY_NOTE = new AppDataKey<DynamicString>("note");
W swojej implementacji
onTileRequest()
wywołaj metodęsetState()
i ustal początkowe mapowania z każdego klucza na konkretną wartość danych dynamicznych:Kotlin
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() )
Java
@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() ); }
Podczas tworzenia układu w miejscu, w którym chcesz wyświetlać dane ze stanu, użyj obiektu typu
Dynamic*
. Możesz też wywołać funkcjęanimate()
, aby wyświetlić animację od poprzedniej do bieżącej wartości:Kotlin
DynamicInt32.from(KEY_WATER_INTAKE).animate()
Java
DynamicInt32.from(KEY_WATER_INTAKE).animate();
W razie potrzeby możesz też zaktualizować stan, dodając nowe wartości. Może być on częścią
LoadAction
kafelka.W tym przykładzie ilość wody zostaje zmieniona na
400
:Kotlin
state.addKeyToValueMapping(KEY_WATER_INTAKE, DynamicDataBuilders.DynamicDataValue.fromInt(400))
Java
state.addKeyToValueMapping(KEY_WATER_INTAKE, DynamicDataBuilders.DynamicDataValue.fromInt(400));