카드 시작하기

앱에서 카드를 제공하려면 앱의 build.gradle 파일에 다음 종속 항목을 포함합니다.

Groovy

dependencies {
    // Use to implement support for wear tiles
    implementation "androidx.wear.tiles:tiles:1.1.0"

    // Use to utilize components and layouts with Material Design in your tiles
    implementation "androidx.wear.tiles:tiles-material:1.1.0"

    // Use to preview wear tiles in your own app
    debugImplementation "androidx.wear.tiles:tiles-renderer:1.1.0"

    // Use to fetch tiles from a tile provider in your tests
    testImplementation "androidx.wear.tiles:tiles-testing:1.1.0"
}

Kotlin

dependencies {
    // Use to implement support for wear tiles
    implementation("androidx.wear.tiles:tiles:1.1.0")

    // Use to utilize components and layouts with Material design in your tiles
    implementation("androidx.wear.tiles:tiles-material:1.1.0")

    // Use to preview wear tiles in your own app
    debugImplementation("androidx.wear.tiles:tiles-renderer:1.1.0")

    // Use to fetch tiles from a tile provider in your tests
    testImplementation("androidx.wear.tiles:tiles-testing:1.1.0")
}

카드 만들기

애플리케이션에서 카드를 제공하려면 다음 코드 샘플과 같이 TileService를 확장하는 클래스를 만들고 메서드를 구현합니다.

Kotlin

private val RESOURCES_VERSION = "1"
class MyTileService : TileService() {
    override fun onTileRequest(requestParams: RequestBuilders.TileRequest) =
        Futures.immediateFuture(Tile.Builder()
            .setResourcesVersion(RESOURCES_VERSION)
            .setTimeline(Timeline.Builder().addTimelineEntry(
                TimelineEntry.Builder().setLayout(
                    Layout.Builder().setRoot(
                        Text.Builder().setText("Hello world!").setFontStyle(
                            FontStyle.Builder().setColor(argb(0xFF000000)).build()
                        ).build()
                    ).build()
                ).build()
            ).build()
        ).build())

    override fun onResourcesRequest(requestParams: ResourcesRequest) =
        Futures.immediateFuture(Resources.Builder()
            .setVersion(RESOURCES_VERSION)
            .build()
        )
}

자바

public class MyTileService extends TileService {
    private static final String RESOURCES_VERSION = "1";

    @NonNull
    @Override
    protected ListenableFuture<Tile> onTileRequest(
        @NonNull TileRequest requestParams
    ) {
        return Futures.immediateFuture(new Tile.Builder()
            .setResourcesVersion(RESOURCES_VERSION)
            .setTimeline(new Timeline.Builder()
                .addTimelineEntry(new TimelineEntry.Builder()
                    .setLayout(new Layout.Builder()
                        .setRoot(new Text.Builder()
                            .setText("Hello world!")
                            .setFontStyle(new FontStyle.Builder()
                                .setColor(argb(0xFF000000)).build()
                            ).build()
                        ).build()
                    ).build()
                ).build()
            ).build()
        );
   }

   @NonNull
   @Override
   protected ListenableFuture<Resources> onResourcesRequest(
       @NonNull ResourcesRequest requestParams
   ) {
       return Futures.immediateFuture(new Resources.Builder()
               .setVersion(RESOURCES_VERSION)
               .build()
       );
   }
}

다음으로, AndroidManifest.xml 파일의 <application> 태그 내부에 서비스를 추가합니다.

<service
   android:name=".MyTileService"
   android:label="@string/tile_label"
   android:description="@string/tile_description"
   android:exported="true"
   android:permission="com.google.android.wearable.permission.BIND_TILE_PROVIDER">
   <intent-filter>
       <action android:name="androidx.wear.tiles.action.BIND_TILE_PROVIDER" />
   </intent-filter>

   <meta-data android:name="androidx.wear.tiles.PREVIEW"
       android:resource="@drawable/tile_preview" />
</service>

권한과 인텐트 필터가 이 서비스를 카드 제공자로 등록합니다.

아이콘, 라벨, 설명은 사용자가 휴대전화나 시계에서 카드를 구성할 때 표시됩니다.

미리보기 메타데이터 태그를 사용하면 휴대전화에서 카드를 구성할 때 카드의 미리보기를 표시할 수 있습니다.

카드 UI 만들기

카드의 레이아웃은 빌더 패턴을 사용하여 작성됩니다. 카드의 레이아웃은 레이아웃 컨테이너와 기본 레이아웃 요소로 이루어진 일종의 트리처럼 구성됩니다. 각 레이아웃 요소에는 다양한 setter 메서드를 통해 설정할 수 있는 속성이 있습니다.

기본 레이아웃 요소

다음과 같은 시각적 요소가 지원됩니다.

  • Text: 텍스트 문자열을 렌더링합니다(선택적으로 줄바꿈 가능).
  • Image: 이미지를 렌더링합니다.
  • Spacer: 요소와 요소 사이에 패딩을 제공하거나 배경 색상을 설정할 때 구분선 역할을 할 수 있습니다.

Material 구성요소

tiles-material 라이브러리는 기본 요소 외에도 Material Design 사용자 인터페이스 권장사항과 일치하는 카드를 디자인할 수 있도록 지원하는 구성요소를 제공합니다.

  • Button: 클릭 가능한 원형 구성요소로, 아이콘을 포함할 수 있습니다.
  • Chip: 클릭 가능한 경기장 모양의 구성요소로, 최대 두 줄의 텍스트와 선택적으로 아이콘을 포함할 수 있습니다.
  • CompactChip: 클릭 가능한 경기장 모양의 구성요소로, 한 줄의 텍스트를 포함할 수 있습니다.
  • TitleChip: 클릭 가능한 경기장 모양의 구성요소로, Chip과 비슷하지만 제목 텍스트를 수용할 수 있도록 높이가 더 큽니다.
  • CircularProgressIndicator: 원형 진행률 표시기로, 화면 가장자리 주위에 진행률을 표시하도록 ProgressIndicatorLayout 내부에 배치할 수 있습니다.

레이아웃 컨테이너

지원되는 컨테이너는 다음과 같습니다.

  • Row: 하위 요소를 가로로 나란히 배치합니다.
  • Column: 하위 요소를 세로로 차례로 배치합니다.
  • Box: 하위 요소를 다른 하위 요소 위에 오버레이합니다.
  • Arc: 하위 요소를 원에 배치합니다.
  • Spannable: 텍스트 섹션에 인터리브 처리 텍스트 및 이미지와 함께 특정 FontStyles를 적용합니다. 자세한 내용은 Spannable을 참고하세요.

모든 컨테이너는 하나 이상의 하위 요소를 포함할 수 있으며, 하위 요소 자체도 컨테이너가 될 수 있습니다. 예를 들어, Column은 여러 개의 Row 요소를 하위 요소로 포함하여 그리드와 같은 레이아웃이 될 수 있습니다.

하나의 컨테이너 레이아웃과 두 개의 하위 레이아웃 요소를 갖는 카드는 다음과 같습니다.

Kotlin

private fun myLayout(): LayoutElement =
    Row.Builder()
        .setWidth(wrap())
        .setHeight(expand())
        .setVerticalAlignment(VALIGN_BOTTOM)
        .addContent(Text.Builder()
            .setText("Hello world")
            .build()
        )
        .addContent(Image.Builder()
            .setResourceId("image_id")
            .setWidth(dp(24f))
            .setHeight(dp(24f))
            .build()
        ).build()

자바

private LayoutElement myLayout() {
    return new Row.Builder()
        .setWidth(wrap())
        .setHeight(expand())
        .setVerticalAlignment(VALIGN_BOTTOM)
        .addContent(new Text.Builder()
            .setText("Hello world")
            .build()
        )
        .addContent(new Image.Builder()
            .setResourceId("image_id")
            .setWidth(dp(24f))
            .setHeight(dp(24f))
            .build()
        ).build();
}

Material 레이아웃

tiles-material 라이브러리는 기본 레이아웃 외에도 요소를 특정 '슬롯'에 배치하는 몇 가지 독자적인 레이아웃을 제공합니다.

  • PrimaryLayout: 단일 기본 작업 CompactChip을 하단에 배치하고 그 위 중앙에 콘텐츠를 배치합니다.
  • MultiSlotLayout: 기본 라벨 및 보조 라벨을 배치하고 그 사이에 선택적인 콘텐츠와 하단에 선택적인 CompactChip을 배치합니다.
  • ProgressIndicatorLayout: 화면 가장자리 주위에 CircularProgressIndicator를 배치하고 그 안에 지정된 콘텐츠를 배치합니다.

지원되는 Arc 컨테이너 하위 요소는 다음과 같습니다.

  • ArcLine: 호를 따라 곡선을 렌더링합니다.
  • ArcText: 호 안에서 곡선 텍스트를 렌더링합니다.
  • ArcAdapter: 호 안의 호에 접하는 지점에 그려지는 기본 레이아웃 요소를 렌더링합니다.

자세한 내용은 각 요소 유형에 관한 참고 문서를 확인하세요.

수정자

사용 가능한 모든 레이아웃 요소에는 선택적으로 수정자를 적용할 수 있습니다. 이러한 수정자의 용도는 다음과 같습니다.

  • 레이아웃의 시각적 모양을 변경합니다. 예를 들어, 레이아웃 요소에 배경, 테두리 또는 패딩을 추가합니다.
  • 레이아웃에 관한 메타데이터를 추가합니다. 예를 들어, 스크린 리더와 함께 사용할 수 있도록 시맨틱 수정자를 레이아웃 요소에 추가합니다.
  • 기능을 추가합니다. 예를 들어, 레이아웃 요소에 클릭 가능한 수정자를 추가하여 대화형 카드를 만듭니다. 자세한 내용은 카드와 상호작용을 참고하세요.

예를 들어, 다음 코드 샘플과 같이 Image의 기본 디자인과 메타데이터를 맞춤설정할 수 있습니다.

Kotlin

private fun myImage(): LayoutElement =
    Image.Builder()
        .setWidth(dp(24f))
        .setHeight(dp(24f))
        .setResourceId("image_id")
        .setModifiers(Modifiers.Builder()
            .setBackground(Background.Builder().setColor(argb(0xFFFF0000)).build())
            .setPadding(Padding.Builder().setStart(dp(12f)).build())
            .setSemantics(Semantics.builder()
                .setContentDescription("Image description")
                .build()
            ).build()
        ).build()

자바

private LayoutElement myImage() {
   return new Image.Builder()
           .setWidth(dp(24f))
           .setHeight(dp(24f))
           .setResourceId("image_id")
           .setModifiers(new Modifiers.Builder()
                   .setBackground(new Background.Builder().setColor(argb(0xFFFF0000)).build())
                   .setPadding(new Padding.Builder().setStart(dp(12f)).build())
                   .setSemantics(new Semantics.Builder()
                           .setContentDescription("Image description")
                           .build()
                   ).build()
           ).build();
}

Spannable

Spannable은 텍스트와 유사한 요소를 배치하는 특별한 유형의 컨테이너입니다. 이 기능은 텍스트의 큰 블록에서 하나의 하위 문자열에만 다른 스타일을 적용하려고 할 때 유용합니다. 이러한 작업은 Text 요소로는 할 수 없습니다.

Spannable 컨테이너는 Span 하위 요소로 채워집니다. 다른 하위 요소 또는 중첩된 Spannable 인스턴스는 허용되지 않습니다.

Span 하위 요소에는 두 가지 유형이 있습니다.

  • SpanText: 텍스트를 특정 스타일로 렌더링합니다.
  • SpanImage: 이미지를 텍스트와 함께 인라인으로 렌더링합니다.

예를 들어, 다음 코드 샘플과 같이 'Hello world' 카드에서 'world'를 기울임꼴로 표시하고 단어 사이에 이미지를 삽입할 수 있습니다.

Kotlin

private fun mySpannable(): LayoutElement =
    Spannable.Builder()
        .addSpan(SpanText.Builder()
            .setText("Hello ")
            .build()
        )
        .addSpan(SpanImage.Builder()
            .setWidth(dp(24f))
            .setHeight(dp(24f))
            .setResourceId("image_id")
            .build()
        )
        .addSpan(SpanText.Builder()
            .setText("world")
            .setFontStyle(FontStyle.Builder()
                .setItalic(true)
                .build())
            .build()
        ).build()

자바

private LayoutElement mySpannable() {
   return new Spannable.Builder()
        .addSpan(new SpanText.Builder()
            .setText("Hello ")
            .build()
        )
        .addSpan(new SpanImage.Builder()
            .setWidth(dp(24f))
            .setHeight(dp(24f))
            .setResourceId("image_id")
            .build()
        )
        .addSpan(new SpanText.Builder()
            .setText("world")
            .setFontStyle(newFontStyle.Builder()
                .setItalic(true)
                .build())
            .build()
        ).build();
}

리소스 사용

카드에서는 앱의 어떤 리소스에도 액세스하지 못합니다. 이는 Android 이미지 ID를 Image 레이아웃 요소에 전달해 확인할 수 없다는 의미입니다. 대신 onResourcesRequest() 메서드를 재정의하고 리소스를 직접 제공하세요.

onResourcesRequest() 메서드 내에 이미지를 제공하는 방법에는 두 가지가 있습니다.

Kotlin

override fun onResourcesRequest(
    requestParams: ResourcesRequest
) = Futures.immediateFuture(
Resources.Builder()
    .setVersion("1")
    .addIdToImageMapping("image_from_resource", ImageResource.Builder()
        .setAndroidResourceByResId(AndroidImageResourceByResId.Builder()
            .setResourceId(R.drawable.image_id)
            .build()
        ).build()
    )
    .addIdToImageMapping("image_inline", ImageResource.Builder()
        .setInlineResource(InlineImageResource.Builder()
            .setData(imageAsByteArray)
            .setWidthPx(48)
            .setHeightPx(48)
            .setFormat(ResourceBuilders.IMAGE_FORMAT_RGB_565)
            .build()
        ).build()
    ).build()
)

자바

@Override
protected ListenableFuture<Resources> onResourcesRequest(
       @NonNull ResourcesRequest requestParams
) {
return Futures.immediateFuture(
    new Resources.Builder()
        .setVersion("1")
        .addIdToImageMapping("image_from_resource", new ImageResource.Builder()
            .setAndroidResourceByResId(new AndroidImageResourceByResId.Builder()
                .setResourceId(R.drawable.image_id)
                .build()
            ).build()
        )
        .addIdToImageMapping("image_inline", new ImageResource.Builder()
            .setInlineResource(new InlineImageResource.Builder()
                .setData(imageAsByteArray)
                .setWidthPx(48)
                .setHeightPx(48)
                .setFormat(ResourceBuilders.IMAGE_FORMAT_RGB_565)
                .build()
            ).build()
        ).build()
);
}