Wear OS에서 첫 카드 만들기

스와이프하여 표시되는 시간, 날씨, 타이머 카드

위 애니메이션(카드 데모)을 확인하세요. 참고: 애니메이션 gif는 한 번만 실행됩니다. 애니메이션을 놓친 경우 페이지를 새로고침하세요.

Wear OS 카드는 사용자가 작업 처리에 필요한 정보와 동작에 쉽게 액세스할 수 있게 해 줍니다. 사용자는 시계 모드에서 간단히 스와이프하여 최근 일기 예보를 찾거나 타이머를 시작할 수 있습니다.

카드 개발은 앱 작성과 약간 다릅니다.

카드는 자체 애플리케이션 컨테이너에서 실행되지 않고 시스템 UI의 일부로 실행됩니다. 즉, Activity 및 XML 레이아웃 같이 익숙한 일부 Android 프로그래밍 개념을 활용하지 않습니다.

여기서는 대신 Service를 사용해 카드의 레이아웃과 콘텐츠를 설명합니다. 그러면 시스템 UI가 필요에 따라 카드를 렌더링합니다.

이 Codelab에서는 Wear OS 카드를 처음부터 작성하는 방법을 알아봅니다.

학습할 내용

  • 카드 만들기
  • 기기에서 카드 테스트
  • 카드 레이아웃 디자인
  • 이미지 추가
  • 상호작용(탭) 추가

빌드할 기능

일일 목표를 달성하기 위한 걸음 수를 보여주는 맞춤 카드를 빌드합니다. 여기에는 다양한 레이아웃 요소와 컨테이너를 배울 수 있는 복잡한 레이아웃이 들어갑니다.

이 Codelab을 마치면 카드가 다음과 같이 표시됩니다.

c6e1959693cded21.png

기본 요건

이 단계에서는 환경을 설정하고 시작 프로젝트를 다운로드해 보겠습니다.

준비 사항

  • Android 스튜디오의 최신 정식 버전
  • Wear OS 기기 또는 에뮬레이터(이것을 처음 사용하시나요? 설정하는 방법이 여기에 나와 있습니다.)

코드 다운로드

git이 설치되어 있으면 아래 명령어를 실행하여 이 저장소의 코드를 클론하면 됩니다. git이 설치되어 있는지 확인하려면 터미널이나 명령줄에 git --version을 입력하여 올바르게 실행되는지 확인합니다.

git clone https://github.com/googlecodelabs/wear-tiles.git
cd wear-tiles

git이 없는 경우 다음 버튼을 클릭하여 이 Codelab을 위한 모든 코드를 다운로드할 수 있습니다.

ZIP 파일 다운로드

언제든지 툴바에서 실행 구성을 변경하여 Android 스튜디오에서 어느 한 모듈을 실행할 수 있습니다.

8a2e49d6d6d2609d.png

Android 스튜디오에서 프로젝트 열기

  1. 'Welcome to Android Studio' 창에서 1f5145c42df4129a.png Open an Existing Project를 선택합니다.
  2. [Download Location] 폴더를 선택합니다.
  3. Android 스튜디오에서 프로젝트를 가져오면 Wear OS 에뮬레이터 또는 실제 기기에서 startfinished 모듈을 실행할 수 있는지 테스트합니다.
  4. start 모듈은 아래 스크린샷과 같이 표시됩니다. 이 모듈에서 모든 작업을 진행할 것입니다.

c72e8870facd8458.png

시작 코드 살펴보기

  • build.gradle에는 기본 앱 구성이 포함되어 있습니다. 카드 생성에 필요한 종속 항목도 포함되어 있습니다.
  • main > AndroidManifest.xml에는 이를 Wear OS 애플리케이션으로 표시하는 데 필요한 요소가 포함되어 있습니다. 이 파일은 이 Codelab에서 검토할 것입니다.
  • main > GoalsRepository.kt에는 사용자가 그날 설정한 임의 걸음 수를 비동기식으로 가져오는 모조 저장소 클래스가 포함되어 있습니다.
  • main > GoalsTileService.kt에는 카드를 만들기 위한 상용구가 포함되어 있습니다. 이 파일에서 대부분의 작업을 처리하게 됩니다.
  • debug > AndroidManifest.xml에는 TilePreviewActivity의 활동 요소가 포함되어 카드를 미리 볼 수 있습니다.
  • debug > TilesPreviewActivity.kt에는 카드를 미리 볼 때 사용하는 Activity가 포함되어 있습니다.

먼저 start 모듈에서 GoalsTileService를 열어 시작합니다. 보시다시피 이 클래스는 TileProviderService를 확장합니다.

TileProviderService는 카드 라이브러리의 일부로, 카드를 작성하는 데 사용하는 다음과 같은 메서드를 제공합니다.

  • onTileRequest(): 시스템에서 요청하면 카드를 만듭니다.
  • onResourcesRequest(): onTileRequest()에 반환된 카드에 필요한 이미지를 제공합니다.

이러한 메서드의 비동기 특성은 코루틴을 사용해 작동하게 됩니다. 코루틴에 관해 자세히 알아보려면 Android 코루틴 문서를 참고하세요.

먼저 간단한 'Hello, world' 카드를 만듭니다.

참고로 본격적으로 진행하기 전에 파일 시작 부분에서 Codelab 전반에 사용할 상수를 다수 정의했습니다. 이러한 상수는 'TODO: Review Constants'를 검색하면 검토할 수 있습니다. 상수는 리소스 버전, dp 값(패딩, 크기 등), sp 값(텍스트), 식별자 등 여러 항목을 정의합니다.

이제 코딩을 시작하겠습니다.

GoalsTileService.kt에서 'TODO: Build a Tile'을 검색하고 전체

onTileRequest() 구현을 아래 코드로 바꿉니다.

1단계

// TODO: Build a Tile.
override fun onTileRequest(requestParams: TileRequest) = serviceScope.future {

    Tile.builder()
        // If there are any graphics/images defined in the Tile's layout, the system will
        // retrieve them using onResourcesRequest() and match them with this version number.
        .setResourcesVersion(RESOURCES_VERSION)

        // Creates a timeline to hold one or more tile entries for a specific time periods.
        .setTimeline(
            Timeline.builder().addTimelineEntry(
                TimelineEntry.builder().setLayout(
                    Layout.builder().setRoot(
                        Text.builder().setText("Hello, world!")
                    )
                )
            )
        ).build()
}

onTileRequest() 메서드에서 Future를 반환할 것으로 예상하므로 serviceScope.future { ... }를 사용하여 코루틴을 자바 ListenableFuture로 변환합니다.

그 외에는 코루틴 내에서 Tile을 생성하기만 하면 됩니다.

onTileRequest() 내에서 빌더 패턴을 사용하여 'Hello, world!' 카드를 만듭니다. 코드 블록의 주석을 꼼꼼히 살펴보고 진행 상황을 확인합니다.

먼저 리소스 버전을 설정합니다. 이 메서드의 페이로드에 반환된 리소스 버전은 실제로 사용된 리소스가 있는지에 관계없이 onResourcesRequest()에서 반환된 페이로드의 리소스 버전과 일치해야 합니다.

이는 그래픽을 가져오기 위해 시스템에서 onResourcesRequest()를 호출할 때 그래픽을 올바른 버전과 일치시키는 방법입니다.

하지만 이 단순한 'Hello, world' 카드에는 그래픽이 없습니다.

다음으로 Timeline을 만듭니다.

Timeline은 하나 이상의 TimelineEntry 인스턴스로 구성됩니다. 각 인스턴스는 특정 시간 구간의 레이아웃을 설명합니다. 개발자는 여러 개의 TimelineEntry 값을 생성해 향후에 발생하도록 할 수 있고, 시스템은 앞으로 그 시간이 되면 관련 값을 렌더링합니다.

fbb666b722376749.png

타임라인 사용에 관한 자세한 내용은 타임라인 가이드를 참고하세요.

이 예에서는 모든 시간과 관련해 카드 하나만 있으면 되기 때문에 TimelineEntry 인스턴스를 하나만 선언했습니다. 그런 다음 setLayout을 사용하여 레이아웃을 설정합니다.

루트는 하나 이상의 복잡한 레이아웃으로 구성할 수 있지만, 이 단계에서는 'Hello, world!'를 표시하는 단순한 Text 레이아웃 요소를 만들어 보겠습니다.

이제 완료됐습니다.

아주 간단한 카드를 만들었으며 이제 Activity에서 미리 보겠습니다.

Activity 내에서 이 카드를 미리 보려면 앱의 debug 소스 폴더에서 TilePreviewActivity를 엽니다.

'TODO: Review creation of Tile for Preview'를 검색한 후 그 뒤에 다음 코드를 추가합니다.

2단계

// TODO: Review creation of Tile for Preview.
tileManager = TileManager(
    context = this,
    component = ComponentName(this, GoalsTileService::class.java),
    parentView = rootLayout
)
tileManager.create()

코드 대부분이 설명이 필요 없을 정도로 자명합니다.

TileManager를 만듭니다. 이 요소는 카드를 미리 보고 컨텍스트와 구성요소(작업 중인 카드 서비스 클래스)는 물론, 카드를 삽입할 상위 뷰를 설정하는 데 사용할 수 있습니다.

그런 다음 create()를 사용하여 카드를 생성합니다.

tileManager.close()를 사용하여 onDestroy()에서 일부 정리 작업도 했습니다.

이제 Wear OS 에뮬레이터나 기기에서 앱을 실행합니다(반드시 start 모듈을 선택해야 함). 화면 중앙에 'Hello, world!'가 표시됩니다.

b9976e1073554422.png

기본 레이아웃이 작동하므로 레이아웃을 확장해 더 복잡한 카드를 만들어 보겠습니다. 최종 목표의 레이아웃은 다음과 같습니다.

c6e1959693cded21.png

루트 레이아웃을 상자로 바꾸기

'Hello, world!'가 포함된 전체 onTileRequest 메서드를 아래 코드로 바꿉니다.

3단계

// TODO: Build a Tile.
override fun onTileRequest(requestParams: TileRequest) = serviceScope.future {

    // Retrieves progress value to populate the Tile.
    val goalProgress = GoalsRepository.getGoalProgress()
    // Retrieves font styles for any text in the Tile.
    val fontStyles = FontStyles.withDeviceParameters(requestParams.deviceParameters)

    // Creates Tile.
    Tile.builder()
        // If there are any graphics/images defined in the Tile's layout, the system will
        // retrieve them using onResourcesRequest() and match them with this version number.
        .setResourcesVersion(RESOURCES_VERSION)
        // Creates a timeline to hold one or more tile entries for a specific time periods.
        .setTimeline(
            Timeline.builder().addTimelineEntry(
                TimelineEntry.builder().setLayout(
                    Layout.builder().setRoot(
                        // Creates the root [Box] [LayoutElement]
                        layout(goalProgress, fontStyles)
                    )
                )
            )
        ).build()
}

코드 블록의 주석을 꼼꼼히 살펴보고 진행 상황을 확인합니다. 아직 layout()을 정의하지 않았으므로 오류는 걱정하지 마세요.

먼저 메서드의 코드 첫 부분에서 (모조) 저장소로부터 실제 목표 진행 상태를 가져옵니다.

또한 onTileRequest()에 전달된 deviceParameters를 가져옵니다. 이 요소는 나중에 텍스트 라벨의 글꼴 스타일을 빌드하는 데 사용합니다.

다음번 코드 청크는 이전과 동일하게 카드를 다시 생성합니다. 거의 대부분의 코드가 같다는 것을 알 수 있습니다.

실제로 루트를 layout(goalProgress, fontStyles)로 설정한 라인만 변경했습니다.

이전에 호출했을 때 메서드 이름 아래에 빨간색 줄이 표시되었습니다. 존재하지 않는 메서드이기 때문입니다.

깊이 중첩되는 코드를 방지하기 위해 레이아웃을 여러 개의 논리 조각으로 나누고 나눈 논리 조각을 자체 메서드에서 정의하는 것은 일반적인 패턴입니다. 그래서 다음으로는 레이아웃 메서드를 정의해 보겠습니다.

'TODO: Create root Box layout and content'를 검색하고 아래 코드를 추가합니다.

4단계

    // TODO: Create root Box layout and content.
    // Creates a simple [Box] container that lays out its children one over the other. In our
    // case, an [Arc] that shows progress on top of a [Column] that includes the current steps
    // [Text], the total steps [Text], a [Spacer], and a running icon [Image].
    private fun layout(goalProgress: GoalProgress, fontStyles: FontStyles) =
        Box.builder()
            // Sets width and height to expand and take up entire Tile space.
            .setWidth(expand())
            .setHeight(expand())

            // Adds an [Arc] via local function.
            .addContent(progressArc(goalProgress.percentage))

            // TODO: Add Column containing the rest of the data.
            // TODO: START REPLACE THIS LATER
            .addContent(
                Text.builder()
                    .setText("REPLACE ME!")
                    .setFontStyle(fontStyles.display3())
            )
            // TODO: END REPLACE THIS LATER

            .build()

코드 블록의 주석을 꼼꼼히 살펴보고 진행 상황을 확인합니다. 아직 progressArc()를 정의하지 않았으므로 그 메서드 호출과 관련해 또는 setRoot() 시 발생하는 오류는 걱정하지 마세요.

레이아웃 메서드에는 하위 요소를 겹겹이 배치하는 단순한 Box 컨테이너가 포함되어 있습니다. 상자 내에 콘텐츠를 추가합니다. 이 경우 아직 존재하지 않는 메서드를 호출하게 됩니다.

복잡한 레이아웃의 메서드를 여러 개로 나누는 패턴을 다시 따를 것입니다. 그러면 레이아웃 읽기가 더 쉽습니다.

코드 하단(build() 호출 위) 근처에 두어 개의 TODO가 추가되었습니다. 이 항목에 관해서는 Codelab 뒷부분에서 살펴보겠습니다.

이제 오류를 해결하고, 우리가 찾고 있는 아치를 최종적으로 선언할 로컬 메서드를 정의하겠습니다.

ArcLine 만들기

먼저 사용자가 걸음 수를 늘리면 채워지는 아치를 화면 가장자리에 만들고자 합니다.

Tiles API는 다양한 Arc 컨테이너 옵션을 제공합니다. Arc 주위에 곡선을 렌더링하는 ArcLine을 사용하겠습니다.

이제 성가신 오류를 제거할 수 있도록 함수를 정의해 보겠습니다.

'TODO: Create a function that constructs an Arc representation of the current step progress'를 찾아 아래 코드를 추가합니다.

5단계

    // TODO: Create a function that constructs an Arc representation of the current step progress.
    // Creates an [Arc] representing current progress towards steps goal.
    private fun progressArc(percentage: Float) = Arc.builder()
        .addContent(
            ArcLine.builder()
                // Uses degrees() helper to build an [AngularDimension] which represents progress.
                .setLength(degrees(percentage * ARC_TOTAL_DEGREES))
                .setColor(argb(ContextCompat.getColor(this, R.color.primary)))
                .setThickness(PROGRESS_BAR_THICKNESS)
        )
        // Element will start at 12 o'clock or 0 degree position in the circle.
        .setAnchorAngle(degrees(0.0f))
        // Aligns the contents of this container relative to anchor angle above.
        // ARC_ANCHOR_START - Anchors at the start of the elements. This will cause elements
        // added to an arc to begin at the given anchor_angle, and sweep around to the right.
        .setAnchorType(ARC_ANCHOR_START)
        .build()

코드 블록의 주석을 꼼꼼히 살펴보고 진행 상황을 확인합니다. 참고: degrees(), argb(), ContextCompat에 관한 오류가 표시될 수 있습니다. 이 경우 카드 버전을 클릭하여 가져오기만 하면 됩니다.

이 코드 부분에서는 걸음 목표 진행 상태를 나타내는 ArcLine이 포함된 Arc.Builder를 반환합니다.

길이를 최종 목표의 백분율(단위: 도로 변환됨), 색상 및 두께로 설정합니다.

다음으로 아치가 앵커 유형과 함께 시작될 위치를 지정합니다. 앵커에는 다양한 옵션이 있으므로, Codelab을 마친 후에 자유롭게 모든 옵션을 둘러보세요.

잘하셨습니다. 이 부분을 끝냈습니다.

어떻게 실행되는지 확인해 보겠습니다. 앱을 다시 실행하면 다음과 같이 표시됩니다.

ad79daf115d3b6a4.png

Column 컨테이너 추가

카드에 멋진 진행률 표시기가 있으므로 적절한 텍스트를 추가해 보겠습니다.

여러 텍스트 필드(및 이후에는 이미지)를 추가할 예정이므로 화면 중앙 아래의 Column에 항목을 배치하고자 합니다.

Column여러 카드 레이아웃 컨테이너 중 하나로, 하위 요소를 세로로 겹쳐서 배치합니다.

'TODO: Add Column containing the rest of the data'를 찾아 임시 텍스트 콘텐츠를 아래 코드로 바꿉니다. TODO: START REPLACE THIS LATER부터 TODO: END REPLACE THIS LATER까지의 모든 항목에 해당합니다.

원본에서 코드 블록 끝에 있는 build() 호출은 삭제하지 마세요.

6단계

            // TODO: Add Column containing the rest of the data.
            // Adds a [Column] containing the two [Text] objects, a [Spacer], and a [Image].
            .addContent(
                Column.builder()
                    // Adds a [Text] using local function.
                    .addContent(
                        currentStepsText(goalProgress.current.toString(), fontStyles)
                    )
                    // Adds a [Text] using local function.
                    .addContent(
                        totalStepsText(
                            resources.getString(R.string.goal, goalProgress.goal),
                            fontStyles
                        )
                    )
                    // TODO: Add Spacer and Image representations of our step graphic.
                    // DO LATER
            )

코드 블록의 주석을 꼼꼼히 살펴보고 진행 상황을 확인합니다.

두 가지 콘텐츠를 추가하여 열을 만듭니다. 지금 오류 2개가 발생했습니다.

이유가 무엇인지 아시겠나요?

다시 한번 복잡한 레이아웃의 메서드를 분리하는 패턴을 따릅니다. DO LATER TODO를 무시해도 됩니다.

이러한 오류를 수정해 보겠습니다.

텍스트 요소 추가

'TODO: Create functions that construct/stylize Text representations of the step count & goal'을 찾아 그 아래에 다음 코드를 추가합니다.

7단계

    // TODO: Create functions that construct/stylize Text representations of the step count & goal.
    // Creates a [Text] with current step count and stylizes it.
    private fun currentStepsText(current: String, fontStyles: FontStyles) = Text.builder()
        .setText(current)
        .setFontStyle(fontStyles.display2())
        .build()

    // Creates a [Text] with total step count goal and stylizes it.
    private fun totalStepsText(goal: String, fontStyles: FontStyles) = Text.builder()
        .setText(goal)
        .setFontStyle(fontStyles.title3())
        .build()

코드 블록의 주석을 꼼꼼히 살펴보고 진행 상황을 확인합니다.

쉽게 알 수 있습니다. 두 개의 다른 함수를 통해 별도의 Text 레이아웃 요소를 만듭니다.

짐작하셨겠지만 Text 레이아웃 요소는 텍스트 문자열을 렌더링(선택적으로는 래핑)합니다.

이전에 이 클래스에서 정의한 상수에서 글꼴 크기를 설정하고, 마지막으로 onTileRequest()에서 가져온 스타일에서 글꼴 스타일을 설정합니다.

이제 텍스트가 생겼습니다. 카드가 이제 어떻게 표시되는지 살펴보겠습니다. 카드를 실행하면 다음과 같이 표시됩니다.

9eaca483c7e51f38.png

onTileRequest()에 이미지 이름 추가

UI의 마지막 부분에 이미지를 추가하겠습니다.

'TODO: Add Spacer and Image representations of our step graphic'을 찾고 DO LATER를 아래 코드로 바꿉니다. 후행 ')' 문자는 삭제하지 마세요.

또한 원본에서 코드 블록 끝에 있는 build() 호출은 삭제하지 마세요.

8단계

                    // TODO: Add Spacer and Image representations of our step graphic.
                    // Adds a [Spacer].
                    .addContent(Spacer.builder().setHeight(VERTICAL_SPACING_HEIGHT))
                    // Adds an [Image] using local function.
                    .addContent(startRunButton())

코드 블록의 주석을 꼼꼼히 살펴보고 진행 상황을 확인합니다.

먼저 Spacer 레이아웃 요소를 추가하여 요소(여기에서는 뒤에 오는 이미지) 간에 패딩을 제공합니다.

다음으로, 이미지를 렌더링하는 Image 레이아웃 요소의 콘텐츠를 추가합니다. 하지만 오류로 예상했듯이 이 요소는 별도의 로컬 함수에서 정의합니다.

'TODO: Create a function that constructs/stylizes a clickable Image of a running icon'을 찾아 아래 코드를 추가합니다.

9단계

    // TODO: Create a function that constructs/stylizes a clickable Image of a running icon.
    // Creates a running icon [Image] that's also a button to refresh the tile.
    private fun startRunButton() =
        Image.builder()
            .setWidth(BUTTON_SIZE)
            .setHeight(BUTTON_SIZE)
            .setResourceId(ID_IMAGE_START_RUN)
            .setModifiers(
                Modifiers.builder()
                    .setPadding(
                        Padding.builder()
                            .setStart(BUTTON_PADDING)
                            .setEnd(BUTTON_PADDING)
                            .setTop(BUTTON_PADDING)
                            .setBottom(BUTTON_PADDING)
                    )
                    .setBackground(
                        Background.builder()
                            .setCorner(Corner.builder().setRadius(BUTTON_RADIUS))
                            .setColor(argb(ContextCompat.getColor(this, R.color.primaryDark)))
                    )
                    // TODO: Add click (START)
                    // DO LATER
                    // TODO: Add click (END)
            )
            .build()

코드 블록을 검토해 진행 상황을 확인합니다.

지금까지 그랬던 것처럼 DO LATER TODO는 무시합니다.

코드 대부분이 설명이 따로 필요 없을 정도로 자명합니다. 클래스 상단에서 정의한 상수를 사용하여 다양한 차원과 스타일을 설정합니다. 수정자에 관해 자세히 알아보려면 여기를 클릭하세요.

여기서 주목할 가장 중요한 요소는 .setResourceId(ID_IMAGE_START_RUN) 호출입니다.

이미지 이름을 클래스 상단에서 정의한 상수로 설정합니다.

마지막 단계로, 이 상수 이름을 애플리케이션의 실제 이미지에 매핑해야 합니다.

onResourcesRequest()에 이미지 매핑 추가

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

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

setAndroidResourceByResId()를 사용하여, 앞서 이미지에 사용한 이름을 실제 이미지에 매핑합니다.

'TODO: Supply resources (graphics) for the Tile'을 찾아 전체 기존 메서드를 아래 코드로 바꿉니다.

10단계

    // TODO: Supply resources (graphics) for the Tile.
    override fun onResourcesRequest(requestParams: ResourcesRequest) = serviceScope.future {
        Resources.builder()
            .setVersion(RESOURCES_VERSION)
            .addIdToImageMapping(
                ID_IMAGE_START_RUN,
                ImageResource.builder()
                    .setAndroidResourceByResid(
                        AndroidImageResourceByResId.builder()
                            .setResourceId(R.drawable.ic_run)
                    )
            )
            .build()
    }

코드 블록을 검토해 진행 상황을 확인합니다.

onTileRequest()를 사용한 첫 번째 단계에서 리소스 버전 번호를 설정한 것이 기억날 것입니다.

여기에서는 카드를 올바른 리소스에 일치하도록 리소스 빌더에서 동일한 리소스 버전 번호를 설정합니다.

그런 다음, 생성한 Image 레이아웃 요소를 addIdToImageMapping() 메서드를 사용하여 실제 이미지에 매핑해야 합니다. 이전과 동일한 상수 이름 ID_IMAGE_START_RUN을 사용한 것을 알 수 있으며 이제 .setResourceId(R.drawable.ic_run)에 반환할 특정 드로어블을 설정합니다.

이제 실행하면 완성된 UI가 표시됩니다.

c6e1959693cded21.png

마지막 단계로 카드에 클릭 작업을 추가하고자 합니다. 그렇게 하면 Wear OS 앱 내에서 Activity를 열거나 이 Codelab에서는 카드 자체로 업데이트를 트리거합니다.

'TODO: Add click (START)'를 찾아 DO LATER 주석을 아래 코드로 변경합니다.

원본에서 코드 블록 끝에 있는 build() 호출은 삭제하지 마세요.

11단계

                    // TODO: Add click (START)
                    .setClickable(
                        Clickable.builder()
                            .setId(ID_CLICK_START_RUN)
                            .setOnClick(ActionBuilders.LoadAction.builder())
                    )
                    // TODO: Add click (END)

코드 블록을 검토해 진행 상황을 확인합니다.

레이아웃 요소에 Clickable 수정자를 추가하면 그 레이아웃 요소를 탭하는 사용자에게 반응할 수 있습니다. 클릭 이벤트 관련 반응으로 다음 두 가지 작업을 할 수 있습니다.

  • LaunchAction: 활동을 시작합니다.
  • LoadAction: onTileRequest()를 호출하여 카드를 강제로 새로고침합니다.

여기에서는 클릭 가능한 수정자를 설정했지만, 카드 자체를 간단한 라인 ActionBuilders.LoadAction.builder()로 새로고침할 때는 LoadAction을 사용합니다. 그렇게 하면 onTileRequest() 호출이 트리거되지만 설정한 ID ID_CLICK_START_RUN이 전달됩니다.

원하는 경우 onTileRequest()에 전달된 마지막 클릭 가능한 ID를 확인하고 그 ID를 기반으로 다른 카드를 렌더링할 수 있습니다. 그러면 다음과 같이 표시됩니다.

12단계

// Example of getting the clickable Id
override fun onTileRequest(requestParams: TileRequest) = serviceScope.future {

    if (requestParams.state.lastClickableId == ID_CLICK_START_RUN) {
        // Create start run tile...
    } else {
        // Create default tile...
    }
}

여기서는 별도의 작업이 필요 없습니다. 카드를 새로고침하기만 하면 됩니다. 그러면 모조 데이터베이스에서 새 값이 가져와 집니다.

카드와 상호작용하는 추가 옵션에 관해 알아보려면 가이드를 검토하세요.

이제 카드를 다시 실행합니다. 버튼을 클릭하면 걸음 값이 변합니다.

e15bba88abc0d832.png

짐작하셨겠지만, 매니페스트 내에서 수정했던 서비스를 정의했습니다.

'TODO: Review service'를 찾으면 아래 코드가 표시됩니다.

여기서는 진행할 단계가 없으며 기존 코드를 검토하기만 하면 됩니다.

12단계

<!-- TODO: Review service -->
<service
   android:name="com.example.wear.tiles.GoalsTileService"
   android:label="@string/fitness_tile_label"
   android:description="@string/tile_description"
   android:icon="@drawable/ic_run"
   android:permission="com.google.android.wearable.permission.BIND_TILE_PROVIDER">
   <intent-filter>
       <action android:name="androidx.wear.tiles.action.BIND_TILE_PROVIDER" />
   </intent-filter>

   <!-- The tile preview shown when configuring tiles on your phone -->
   <meta-data
       android:name="androidx.wear.tiles.PREVIEW"
       android:resource="@drawable/tile_goals" />
</service>

코드 블록을 검토해 진행 상황을 확인합니다.

이는 일반 서비스와 유사하지만 카드를 위한 다음과 같은 특정 요소를 몇 가지 추가했습니다.

  1. 카드 제공자를 바인딩할 권한
  2. 서비스를 카드 제공자로 등록하는 인텐트 필터
  3. 휴대전화에 표시될 미리보기 이미지를 지정하는 추가 메타데이터

축하합니다. 지금까지 Wear OS용 카드를 빌드하는 방법을 알아보았습니다.

다음 단계

다른 Wear OS Codelab을 확인해 보세요.

추가 자료