카드에 동적 업데이트 표시

Tiles 1.2부터는 동적 표현식을 사용하여 플랫폼 데이터 업데이트를 스트리밍할 수 있습니다. 그런 다음 이러한 업데이트를 카드의 애니메이션에 연결할 수 있습니다. 앱에는 1초에 한 번 이 값의 업데이트가 제공됩니다.

동적 표현식을 사용하면 콘텐츠가 변경되어도 카드 전체를 새로고침할 필요가 없습니다. 카드에서 보다 시선을 사로잡는 경험을 만들려면 이러한 동적 객체에 애니메이션을 적용하세요.

동적 표현식을 데이터 소스에 연결

androidx.wear.protolayoutandroidx.wear.protolayout.material 네임스페이스에는 필드에서 동적 표현식을 받는 클래스가 여럿 포함되어 있습니다. 몇 가지 예는 다음과 같습니다.

카드에 포함된 요소의 한 가지 가능한 값으로 동적 표현식을 사용하려면, 요소의 대응되는 *Prop 동적 속성 유형을 사용하고 동적 속성 유형의 빌더 클래스의 setDynamicValue() 메서드에 데이터 소스를 전달합니다.

카드는 다음과 같은 동적 속성 유형을 지원합니다.

실제 크기(색상을 제외한 카드의 모든 값)에 영향을 주는 동적 표현식을 사용할 때는 문자열 형식과 같은 관련 제약 조건도 지정해야 합니다. 이러한 제약 조건을 사용하면 시스템 렌더기가 카드 내에서 값이 차지할 수 있는 최대 공간을 확인할 수 있습니다. 일반적으로 이러한 제약 조건은 setLayoutConstraintsForDynamic*으로 시작하는 메서드를 호출하여 동적 표현식 수준이 아닌 요소 수준에서 지정해야 합니다.

다음 코드 스니펫은 3자리 숫자와 대체 값 --를 사용하여 심박수 업데이트를 표시하는 방법을 보여줍니다.

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

하나의 카드에서 적은 수의 표현식 사용

Wear OS는 하나의 카드에 포함할 수 있는 표현식의 수에 제한을 둡니다. 카드에 동적 표현식이 너무 많으면 동적 값이 무시되고 개발자가 각 동적 속성 유형에 제공한 정적 값으로 대체됩니다.

아래에서는 표현식의 개수가 그다지 많지 않으므로 안심하고 카드에 다음 표현식 세트를 추가할 수 있습니다. 따라서 카드가 올바르게 동작합니다.

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");

하지만 이 카드에는 표현식이 너무 많습니다.

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

동적 데이터를 상태 객체로 통합

데이터 소스의 최신 업데이트 집합을 상태로 통합하고 값 렌더링을 위해 이 값을 카드에 전달할 수 있습니다.

카드에서 상태 정보를 사용하려면 다음 단계를 따릅니다.

  1. 카드 상태의 여러 값을 나타내는 키 집합을 설정합니다. 이 예시에서는 물 섭취 키와 메모 키를 만듭니다.

    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");
    
  2. onTileRequest() 구현에서 setState()를 호출하고 각 키에서 특정 동적 데이터 값으로의 초기 매핑을 설정합니다.

    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()
        );
    }
    
  3. 레이아웃을 만들 때는 상태의 데이터를 표시할 위치에서 Dynamic* 유형 객체를 사용합니다. animate()를 호출하여 이전 값에서 현재 값으로의 애니메이션을 보여줄 수도 있습니다.

    Kotlin

    DynamicInt32.from(KEY_WATER_INTAKE).animate()
    

    Java

    DynamicInt32.from(KEY_WATER_INTAKE).animate();
    
  4. 필요한 경우 상태를 새 값으로 업데이트할 수도 있습니다. 이는 카드의 LoadAction에 포함할 수 있습니다.

    이 예시에서는 물 섭취 값이 400으로 업데이트됩니다.

    Kotlin

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

    Java

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