Wear OS용 Compose Codelab

1. 소개

11ba5a682f1ffca3.png

Wear OS용 Compose를 사용하면 Jetpack Compose로 앱을 빌드하는 것과 관련해 학습한 지식을 웨어러블 기기에 적용할 수 있습니다.

Material You 지원이 내장된 Wear OS용 Compose는 UI 개발을 간소화 및 가속화하며 코딩 작업을 줄이고도 훌륭한 앱을 개발할 수 있게 해 줍니다.

이 Codelab에서는 Compose에 관해 전문가 수준은 아니지만 어느 정도의 지식이 필요합니다.

Google에서는 Jetpack Compose를 기반으로 빌드된 오픈소스 프로젝트인 Horologist를 사용하여 개발자의 앱 개발 가속화를 지원할 예정입니다.

여기서는 몇 가지 Wear 전용 컴포저블(단순한 것과 복잡한 것 모두)을 만들게 됩니다. 과정을 마치면 나만의 Wear OS용 앱을 작성할 수 있습니다. 그럼 시작해 볼까요?

학습할 내용

  • 이전 Compose 환경 간의 유사점과 차이점
  • 간단한 컴포저블 및 컴포저블이 Wear OS에서 작동하는 방식
  • Wear OS 전용 컴포저블
  • Wear OS의 LazyColumn(ScalingLazyColumn)
  • Wear OS의 Scaffold 버전

빌드할 항목

Wear OS에 최적화된 컴포저블에 대한 스크롤 가능한 목록을 표시하는 간단한 앱을 빌드하게 됩니다.

Scaffold를 사용하기 때문에, 곡선 텍스트 시간(상단에 표시됨)과 비네트, 마지막으로 스크롤 표시기(기기 측면에 표시됨)도 갖게 됩니다.

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

31cb08c0fa035400.gif

기본 요건

2. 설정하기

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

준비 사항

  • Android 스튜디오의 최신 안정화 버전
  • Wear OS 기기 또는 에뮬레이터(에뮬레이터를 처음 사용하시나요? 설정하는 방법을 여기에서 알아보세요.)

코드 다운로드

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

git clone https://github.com/android/codelab-compose-for-wear-os.git
cd compose-for-wear-os

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

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

400c194c8948c952.png

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

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

c82b07a089099c4f.png

시작 코드 살펴보기

  • build.gradle에는 기본 앱 구성이 포함되어 있습니다. Composable Wear OS 앱을 만드는 데 필요한 종속 항목도 포함되어 있습니다. Jetpack Compose와 Wear OS 버전 간의 유사점과 차이점에 관해 알아보겠습니다.
  • main > AndroidManifest.xml에는 Wear OS 애플리케이션을 만드는 데 필요한 요소가 포함되어 있습니다. 이 앱은 비 Compose 앱과는 동일하고 모바일 앱과도 비슷하기 때문에 따로 살펴보지 않겠습니다.
  • main > theme/ 폴더에는 Compose에서 테마용으로 사용되는 Color, TypeTheme 파일이 포함되어 있습니다.
  • main > MainActivity.kt에는 Compose로 앱을 만들 때 사용하는 상용구가 포함되어 있습니다. 또한 앱의 최상위 컴포저블(예: ScaffoldScalingLazyList)도 포함되어 있습니다.
  • main > ReusableComponents.kt에는 여기서 만들 대부분의 Wear 전용 컴포저블을 위한 함수가 포함되어 있습니다. 이 파일에서 상당수의 작업을 처리하게 됩니다.

3. 종속 항목 검토

Wear와 관련된 종속 항목 변경은 대부분 최상위 아키텍처 레이어에서 이루어집니다(아래에 빨간색으로 강조 표시됨).

d64d9c262a79271.png

즉, Jetpack Compose와 함께 이미 사용하는 많은 종속 항목은 Wear OS를 타겟팅할 때 변경되지 않습니다. 예를 들어 UI, Runtime, Compiler, Animation 종속 항목은 동일하게 유지됩니다.

그러나 이전에 사용한 라이브러리와 다른 적절한 Wear OS Material, FoundationNavigation 라이브러리를 사용해야 합니다.

아래 비교 내용을 참고하여 차이점을 확인할 수 있습니다.

Wear OS 종속 항목(androidx.wear.*)

비교

표준 종속 항목(androidx.*)

androidx.wear.compose:compose-material

오른쪽 대신

androidx.compose.material:material

androidx.wear.compose:compose-navigation

오른쪽 대신

androidx.navigation:navigation-compose

androidx.wear.compose:compose-foundation

오른쪽 항목에 추가

androidx.compose.foundation:foundation

androidx.wear.compose:compose-ui-tooling

오른쪽 항목에 추가

androidx.compose.ui:ui-tooling-preview

1. 개발자는 Wear Compose Material 라이브러리로 확장된 머티리얼 리플, 머티리얼 아이콘 등 기타 머티리얼 관련 라이브러리를 계속 사용할 수 있습니다.

build.gradle을 열고 start 모듈에서 'TODO: Review Dependencies'를 검색합니다. 이 단계는 종속 항목을 검토하기 위한 것으로, 코드를 추가하지는 않습니다.

start/build.gradle:

// TODO: Review Dependencies
def composeBom = platform(libs.androidx.compose.bom)

// General compose dependencies
implementation composeBom
implementation libs.androidx.activity.compose
implementation libs.compose.ui.tooling.preview

implementation(libs.androidx.material.icons.extended)

// Compose for Wear OS Dependencies
implementation libs.wear.compose.material

// Foundation is additive, so you can use the mobile version in your Wear OS app.
implementation libs.wear.compose.foundation

// Compose preview annotations for Wear OS.
implementation(libs.androidx.compose.ui.tooling)

debugImplementation libs.compose.ui.tooling
debugImplementation libs.androidx.ui.test.manifest
debugImplementation composeBom

일반 Compose 종속 항목 대부분은 이미 알고 계시므로 여기서는 따로 다루지 않겠습니다.

Wear OS 종속 항목으로 넘어가겠습니다.

앞서 간략히 언급한 것처럼 material의 Wear OS 전용 버전(androidx.wear.compose:compose-material)만 포함되어 있습니다. 즉, 프로젝트에 androidx.compose.material:material이 표시되거나 포함되지 않습니다.

개발자가 Wear Material과 함께 다른 머티리얼 라이브러리를 사용할 수 있다는 점을 알려야 합니다. 이를 위해 실제로 이 Codelab에서는 androidx.compose.material:material-icons-extended를 포함합니다.

마지막으로, Compose용 Wear foundation 라이브러리(androidx.wear.compose:compose-foundation)를 포함합니다. 이는 추가되는 항목이기 때문에 이전에 사용한 표준 foundation과 함께 사용할 수 있습니다. 이 항목이 일반 Compose 종속 항목에 포함되었다는 것을 이미 알고 계실 수도 있습니다.

이제 종속 항목을 이해했으므로 기본 앱을 살펴보겠습니다.

4. MainActivity 검토

모든 작업을

start

모듈에서 진행하므로, 모든 파일이 이 모듈에 열려 있는지 확인하세요.

먼저 start 모듈에서 MainActivity를 열어 시작합니다.

이는 ComponentActivity를 확장하고 setContent { WearApp() }을 사용하여 UI를 만드는 상당히 간단한 클래스입니다.

Compose에 관한 사전 지식이 있다면 이 클래스가 친숙할 것입니다. 단순히 UI를 설정하기만 합니다.

WearApp() 구성 가능한 함수까지 아래로 스크롤합니다. 코드 자체에 관해 설명하기 전에, 코드 전반에 걸쳐 다수의 TODO가 산재해 있음을 알 수 있습니다. 각각은 이 Codelab의 단계를 나타냅니다. 지금은 이를 무시해도 됩니다.

예를 들면 다음과 같습니다.

재미있는 WearApp()의 코드:

WearAppTheme {
     /* *************************** Part 4: Wear OS Scaffold *************************** */
    // TODO (Start): Create a AppScaffold (Wear Version)

    // TODO: Swap to ScalingLazyColumnState
    val listState = rememberLazyListState()

    /* *************************** Part 4: Wear OS Scaffold *************************** */
    // TODO (Start): Create a ScreenScaffold (Wear Version)

    // Modifiers used by our Wear composables.
    val contentModifier = Modifier.fillMaxWidth().padding(bottom = 8.dp)
    val iconModifier = Modifier.size(24.dp).wrapContentSize(align = Alignment.Center)

    /* *************************** Part 3: ScalingLazyColumn *************************** */
    // TODO: Swap a ScalingLazyColumn (Wear's version of LazyColumn)
    LazyColumn(
        modifier = Modifier.fillMaxSize(),
        contentPadding = PaddingValues(
            top = 32.dp,
            start = 8.dp,
            end = 8.dp,
            bottom = 32.dp,
        ),
        verticalArrangement = Arrangement.Center,
        state = listState,
    ) {
        // TODO: Remove item; for beginning only.
        item { StartOnlyTextComposables() }

        /* ******************* Part 1: Simple composables ******************* */
        item { ButtonExample(contentModifier, iconModifier) }
        item { TextExample(contentModifier) }
        item { CardExample(contentModifier, iconModifier) }

        /* ********************* Part 2: Wear unique composables ********************* */
        item { ChipExample(contentModifier, iconModifier) }
        item { ToggleChipExample(contentModifier) }
        }

    // TODO (End): Create a ScreenScaffold (Wear Version)
    // TODO (End): Create a AppScaffold (Wear Version)
    }

먼저 테마인 WearAppTheme { }을 설정합니다. 이는 이전에 테마를 작성한 방식과 동일합니다. 즉, 색상, 서체, 모양과 함께 MaterialTheme을 설정합니다.

하지만 Wear OS의 경우 일반적으로 원형 기기에 최적화된 기본 Material Wear 모양을 사용하는 것이 좋습니다. 그런 이유로, theme/Theme.kt를 자세히 살펴보면 모양이 재정의되지 않음을 알 수 있습니다.

원하는 경우 theme/Theme.kt를 열어 자세히 살펴볼 수 있지만, 다시 언급하자면 휴대전화에서와 동일합니다.

다음으로, 확장할 Wear 컴포저블을 매번 지정할 필요가 없도록 이러한 컴포저블을 위한 몇 가지 수정자를 생성합니다. 주로 콘텐츠를 중앙에 배치하고 패딩을 추가합니다.

그런 다음 LazyColumn을 만듭니다. 이는 이전에 한 것과 마찬가지로 다수 항목의 세로 스크롤 목록을 생성하는 데 사용됩니다.

코드:

item { StartOnlyTextComposables() }

/* ******************* Part 1: Simple composables ******************* */
item { ButtonExample(contentModifier, iconModifier) }
item { TextExample(contentModifier) }
item { CardExample(contentModifier, iconModifier) }

/* ********************* Part 2: Wear unique composables ********************* */
item { ChipExample(contentModifier, iconModifier) }
item { ToggleChipExample(contentModifier) }

항목 자체와 관련해서는 StartOnlyTextComposables()는 UI만 생성합니다. 나머지는 Codelab을 진행하며 채우게 됩니다.

이러한 함수는 사실 다음 섹션에서 찾게 될 ReusableComponents.kt 파일에 있습니다.

Wear OS용 Compose를 시작해 보겠습니다.

5. 간단한 컴포저블 추가

아마도 이미 잘 알고 계실 세 가지 컴포저블(Button, Text, Card)부터 시작해 보겠습니다.

먼저 Hello World 컴포저블을 삭제하겠습니다.

'TODO: Remove item'를 검색하고 주석과 주석 아래의 행을 모두 삭제합니다.

1단계

// TODO: Remove item; for beginning only.
item { StartOnlyTextComposables() }

다음으로, 첫 번째 컴포저블을 추가해 보겠습니다.

Button 컴포저블 만들기

start 모듈에서 ReusableComponents.kt를 열고 'TODO: Create a Button Composable'을 검색한 다음 현재 구성 가능한 메서드를 다음 코드로 바꿉니다.

2단계

// TODO: Create a Button Composable (with a Row to center)
@Composable
fun ButtonExample(
    modifier: Modifier = Modifier,
    iconModifier: Modifier = Modifier
) {
    Row(
        modifier = modifier,
        horizontalArrangement = Arrangement.Center
    ) {
        // Button
        Button(
            modifier = Modifier.size(ButtonDefaults.LargeButtonSize),
            onClick = { /* ... */ },
        ) {
            Icon(
                imageVector = Icons.Rounded.Phone,
                contentDescription = "triggers phone action",
                modifier = iconModifier
            )
        }
    }
}

ButtonExample() 구성 가능한 함수(이 코드가 있는 위치)가 이제 가운데 버튼을 생성합니다.

코드를 살펴보겠습니다.

여기서 RowButton 컴포저블을 원형 화면 중앙에 배치하는 데만 사용됩니다. 이 작업은 MainActivity에서 만든 수정자를 적용하고 이를 이 함수에 전달하여 확인할 수 있습니다. 나중에 원형 화면에서 스크롤할 때 콘텐츠가 잘리지 않도록(화면 중앙에 배치하는 이유임) 할 수 있습니다.

다음으로, Button을 직접 만듭니다. 코드는 이전에 Button에 사용한 것과 동일하지만 여기서는 ButtonDefault.LargeButtonSize를 사용합니다. Wear OS 기기에 최적화된 미리 설정된 크기이므로 반드시 사용해야 합니다.

그다음, 클릭 이벤트를 빈 람다에 설정합니다. 여기서 이러한 컴포저블은 데모용이므로 나중에는 필요하지 않습니다. 하지만 실제 앱에서는 비즈니스 로직을 실행하기 위해 ViewModel 등과 통신하게 됩니다.

그런 다음 버튼 내부에 Icon을 설정합니다. 코드는 이전에 Icon과 관련해 본 것과 동일합니다. androidx.compose.material:material-icons-extended 라이브러리에서도 아이콘을 가져옵니다.

마지막으로 이전에 설정한 수정자를 Icon에 설정합니다.

앱을 실행하면 다음과 같이 표시됩니다.

c9b981101ae653db.png

이 코드는 이전에 이미 작성해 놓았을 것입니다(매우 좋죠). 차이점은 이제 Wear OS에 최적화된 버튼이 생성되었다는 점입니다.

매우 간단합니다. 다른 것을 하나 더 살펴보겠습니다.

Text 컴포저블 만들기

ReusableComponents.kt에서 'TODO: Create a Text Composable'을 검색하고 현재 구성 가능한 메서드를 다음 코드로 바꿉니다.

3단계

// TODO: Create a Text Composable
@Composable
fun TextExample(modifier: Modifier = Modifier) {
    Text(
        modifier = modifier,
        textAlign = TextAlign.Center,
        color = MaterialTheme.colors.primary,
        text = stringResource(R.string.device_shape)
    )
}

Text 컴포저블을 만들고 수정자를 설정하고 텍스트를 정렬한 다음, 색상을 설정하고 마지막으로 문자열 리소스에서 텍스트 자체를 설정합니다.

Text 컴포저블은 Compose 개발자에게 매우 익숙할 것이며, 실제로 코드가 이전에 사용한 코드와 동일합니다.

그 모습을 직접 살펴보겠습니다.

b33172e992d1ea3e.png

이제 TextExample() 구성 가능한 함수(코드를 배치한 위치)는 기본 머티리얼 색상으로 Text 컴포저블을 생성합니다.

문자열은 res/values/strings.xml 파일에서 가져오게 됩니다. 실제로 res/values 폴더를 보면 두 개의 strings.xml 리소스 파일이 있습니다.

지금까지는 꽤 순조로웠습니다. 유사한 컴포저블인 Card를 마지막으로 살펴보겠습니다.

Card 컴포저블 만들기

ReusableComponents.kt에서 'TODO: Create a Card'를 검색하고 현재 구성 가능한 메서드를 다음 코드로 바꿉니다.

4단계

// TODO: Create a Card (specifically, an AppCard) Composable
@Composable
fun CardExample(
    modifier: Modifier = Modifier,
    iconModifier: Modifier = Modifier
) {
    AppCard(
        modifier = modifier,
        appImage = {
            Icon(
                imageVector = Icons.Rounded.Message,
                contentDescription = "triggers open message action",
                modifier = iconModifier
            )
        },
        appName = { Text("Messages") },
        time = { Text("12m") },
        title = { Text("Kim Green") },
        onClick = { /* ... */ }
    ) {
        Text("On my way!")
    }
}

Wear는 AppCardTitleCard라는 두 개의 기본 카드가 있다는 점에서 조금 다릅니다.

여기서는 카드에 Icon이 필요하므로 AppCard를 사용하겠습니다. TitleCard의 슬롯이 더 적습니다. 자세한 내용은 카드 가이드를 참고하세요.

AppCard 컴포저블을 만들고 수정자를 설정하고 Icon을 추가한 다음, 몇 가지 Text 컴포저블 매개변수(카드의 영역마다 하나씩)를 추가하고 마지막으로 맨 끝에 기본 콘텐츠 텍스트를 설정합니다.

그 모습을 직접 살펴보겠습니다.

1fc761252ac5b466.png

이 지점에서 이러한 컴포저블의 Compose 코드가 사실상 이전에 사용한 것과 동일하다는 것을 알 수 있습니다. 멋지지 않나요? 이미 배운 모든 지식을 그대로 다시 활용할 수 있습니다.

이제 새로운 몇 가지 컴포저블을 살펴보겠습니다.

6. Wear에 고유한 컴포저블 추가

이 섹션에서는 ChipToggleChip 컴포저블을 살펴봅니다.

Chip 컴포저블 만들기

Chip은 머티리얼 가이드라인에 실제로 명시되어 있습니다. 하지만 표준 머티리얼 라이브러리에는 실제 구성 가능한 함수가 없습니다.

Chip은 빠른 원탭 동작을 위해 만들어진 것으로, 화면 공간이 제한된 Wear 기기에 특히 적합합니다.

다음은 Chip 구성 가능한 함수의 두어 가지 변형입니다. 이를 통해 여러분이 만들 수 있는 항목에 관한 아이디어를 얻을 수 있습니다.

코드를 작성해 보겠습니다.

ReusableComponents.kt에서 'TODO: Create a Chip'을 검색하고 현재 구성 가능한 메서드를 다음 코드로 바꿉니다.

5단계

// TODO: Create a Chip Composable
@Composable
fun ChipExample(
    modifier: Modifier = Modifier,
    iconModifier: Modifier = Modifier
) {
    Chip(
        modifier = modifier,
        onClick = { /* ... */ },
        label = {
            Text(
                text = "5 minute Meditation",
                maxLines = 1,
                overflow = TextOverflow.Ellipsis
            )
        },
        icon = {
            Icon(
                imageVector = Icons.Rounded.SelfImprovement,
                contentDescription = "triggers meditation action",
                modifier = iconModifier
            )
        },
    )
}

Chip 컴포저블은 다른 컴포저블(예: 수정자 및 onClick)에 사용되는 매개변수 중 상당수를 그대로 사용하므로 매개변수에 관해서는 살펴볼 필요가 없습니다.

이 컴포저블도 라벨(생성하는 Text 컴포저블이 사용되는 대상)과 아이콘을 사용합니다.

Icon 코드는 다른 컴포저블에서 본 코드와 정확히 동일해야 하지만, 이 경우에는 androidx.compose.material:material-icons-extended 라이브러리에서 Self Improvement 아이콘을 가져옵니다.

그 모습을 직접 살펴보겠습니다(아래로 스크롤해야 함).

d97151e85e9a1e03.png

이제 Toggle의 변형인 ToggleChip 컴포저블을 살펴보겠습니다.

ToggleChip 컴포저블 만들기

ToggleChipChip과 유사하지만 사용자가 라디오 버튼, 전환 버튼 또는 체크박스와 상호작용할 수 있도록 해 줍니다.

ReusableComponents.kt에서 'TODO: Create a ToggleChip'을 검색하고 현재 구성 가능한 메서드를 다음 코드로 바꿉니다.

6단계

// TODO: Create a ToggleChip Composable
@Composable
fun ToggleChipExample(modifier: Modifier = Modifier) {
    var checked by remember { mutableStateOf(true) }
    ToggleChip(
        modifier = modifier,
        checked = checked,
        toggleControl = {
            Switch(
                checked = checked,
                modifier = Modifier.semantics {
                    this.contentDescription = if (checked) "On" else "Off"
                }
            )
        },
        onCheckedChange = {
            checked = it
        },
        label = {
            Text(
                text = "Sound",
                maxLines = 1,
                overflow = TextOverflow.Ellipsis
            )
        }
    )
}

이제 ToggleChipExample() 구성 가능한 함수(이 코드가 있는 위치)가 체크박스나 라디오 버튼이 아닌 전환 버튼을 사용하여 ToggleChip을 생성합니다.

먼저 MutableState를 만듭니다. 다른 함수에서는 이 작업을 하지 않았습니다. 여기서는 Wear의 기능을 확인하기 위해 주로 UI 데모를 실행하기 때문입니다.

일반 앱에서는 아마도 컴포저블을 스테이트리스(Stateless)로 만들기 위해, 앱 처리를 위해 선택한 상태와 람다를 전달할 수 있을 것입니다(자세한 내용은 여기 참고).

여기서는 작동하는 전환 버튼과 함께 ToggleChip이 어떻게 동작하는지 보여주기 위해 컴포저블을 단순한 형태로 유지합니다.

다음으로, 원하는 스위치를 구현하기 위해 수정자와 선택한 상태, 전환 컨트롤을 설정합니다.

그런 다음 상태 변경을 위한 람다를 만들고 마지막으로 Text 컴포저블과 일부 기본 매개변수를 갖는 라벨을 설정합니다.

그 모습을 직접 살펴보겠습니다.

ea1a76abd54877b.png

지금까지 많은 Wear OS 전용 컴포저블을 살펴보았습니다. 앞서 언급한 것처럼 대부분의 코드는 이전에 작성한 것과 거의 동일합니다.

이제 좀 더 고급 내용을 살펴보겠습니다.

7. ScalingLazyColumn으로 이전

아마도 모바일 앱에서는 LazyColumn을 사용해 세로로 스크롤되는 목록을 만들었을 것입니다.

원형 기기는 상단과 하단이 더 좁기 때문에 항목을 표시할 공간이 적습니다. 그런 이유로 이러한 둥근 기기에 관한 지원을 높이기 위해 Wear OS에는 자체 버전의 LazyColumn이 있습니다.

ScalingLazyColumnLazyColumn을 확장하여 화면 상단과 하단에서의 크기 조정과 투명도를 모두 지원합니다. 이를 통해 사용자는 콘텐츠를 더 쉽게 읽을 수 있습니다.

다음은 데모입니다.

198ee8e8fa799f08.gif

항목이 중심에 가까울수록 전체 크기로 확장된 다음, 중심에서 멀어지면 (더 투명해지면서) 크기가 다시 줄어드는 모습을 볼 수 있습니다.

다음은 앱에 실제로 표시되는 보다 구체적인 예입니다.

a5a83ab2e5d5230f.gif

이렇게 하면 실제로 가독성에 큰 도움이 되는 것을 알 수 있습니다.

ScalingLazyColumn의 동작을 확인했으므로 이제 LazyColumn을 변환해 보겠습니다.

Horologist ScalinglazyColumn을 사용하여 목록의 항목에 올바른 패딩이 있고 다양한 기기 화면 크기에서 잘리지 않도록 합니다.

Horologist ScalingLazyColumnState로 변환

MainActivity.kt에서 'TODO: Swap to ScalingLazyColumnState'를 검색하고 해당 주석과 그 아래 행을 다음 코드로 바꿉니다. 최적의 패딩 값을 사용하여 콘텐츠가 잘리는 것을 방지하기 위해 첫 번째와 마지막 구성요소를 어떻게 지정하는지 참고하세요.

7단계

// TODO: Swap to ScalingLazyColumnState
val listState = rememberResponsiveColumnState(
    contentPadding = ScalingLazyColumnDefaults.padding(
        first = ItemType.SingleButton,
        last = ItemType.Chip,
    ),
)

이름은 거의 동일합니다. LazyListStateLazyColumn의 상태를 처리하는 것처럼 ScalingLazyColumnStateScalingLazyColumn의 상태를 처리합니다.

Horologist ScalingLazyColumn으로 변환

그다음, ScalingLazyColumn으로 바꿉니다.

MainActivity.kt에서 'TODO: Swap a ScalingLazyColumn'을 검색합니다. 먼저 LazyColumn을 Horologist ScalingLazyColumn으로 대체합니다.

그런 다음 contentPadding, verticalArrangement, modifier, autocentering을 모두 삭제합니다. Horologist ScalingLazyColumn은 이미 대부분의 표시 영역이 목록 항목으로 채워지므로 기본적으로 더 나은 시각적 효과를 보장하는 기본 설정을 제공합니다. 대부분의 경우 기본 매개변수로 충분합니다. 상단에 헤더가 있다면 ResponsiveListHeader에 첫 번째 항목으로 배치하는 것이 좋습니다.

8단계

// TODO: Swap a ScalingLazyColumn (Wear's version of LazyColumn)
ScalingLazyColumn(
    columnState = listState
)

이제 완료됐습니다. 그 모습을 직접 살펴보겠습니다.

5c25062081307944.png

아주 조금씩 스크롤하면서 이동하면 화면 상단과 하단에서 콘텐츠 크기와 투명도가 조정되는 것을 확인할 수 있습니다.

콘텐츠를 위아래로 움직이면 콘텐츠가 명상 컴포저블과 함께 표시됩니다.

이제 마지막 주제인 Wear OS의 Scaffold로 넘어가겠습니다.

8. Scaffold 추가

Scaffold는 모바일과 같은 일반적인 패턴으로 화면을 정렬할 수 있는 레이아웃 구조를 제공합니다. 하지만, 앱 바, FAB, 창 또는 기타 모바일 관련 요소 대신 최상위 구성요소를 사용하여 Wear에 특화된 네 가지 레이아웃(시간, 스크롤/위치 표시기, 페이지 표시기)을 지원합니다.

다음과 같이 구성되어 있습니다.

TimeText

PositionIndicator

PageIndicator

처음 세 가지 구성요소를 자세히 살펴보기 전에 먼저 Scaffold를 제자리에 배치해 보겠습니다. Horologist AppScaffoldScreenScaffold를 사용하여 화면에 TimeText를 기본으로 추가하고 화면 간 이동 시 올바르게 애니메이션 처리되도록 합니다. 또한 ScreenScaffold는 스크롤해야 하는 콘텐츠를 위해 PositionIndicator를 추가합니다.

Scaffold 추가

이제 AppScaffoldScreenScaffold의 상용구를 추가해 보겠습니다.

'TODO (Start): Create a AppScaffold (Wear Version)'을 찾아 그 아래에 다음 코드를 추가합니다.

9단계

WearAppTheme {
// TODO (Start): Create a Horologist AppScaffold (Wear Version)
AppScaffold {

'TODO (Start): Create a ScreenScaffold (Wear Version)'을 찾아 그 아래에 다음 코드를 추가합니다.

// TODO (Start): Create a Horologist ScreenScaffold (Wear Version)
ScreenScaffold(
    scrollState = listState,
){

다음으로, 올바른 위치에 오른쪽 괄호를 추가합니다.

'TODO (End): Create a ScreenScaffold (Wear Version)'을 찾고 그곳에 오른쪽 괄호를 추가합니다.

10단계

// TODO (End): Create a ScreenScaffold (Wear Version)
}

'TODO (End): Create a AppScaffold (Wear Version)'을 찾고 그곳에 오른쪽 괄호를 추가합니다.

10단계

// TODO (End): Create a AppScaffold (Wear Version)
}

먼저 실행해 보겠습니다. 다음과 같이 표시됩니다.

ff554156bbe03abb.png

다음을 추가합니다.

  • TimeText. 내부에 곡선 텍스트를 사용하고 개발자가 컴포저블을 배치하거나 시간 관련 클래스를 사용하지 않고도 손쉽게 시간을 표시할 수 있게 해줍니다. 또한 Material 가이드라인에 따라 시간 표시는 앱 내 화면 상단에 하는 것이 좋습니다. 그러면 스크롤하는 동안 시간이 사라집니다.
  • PositionIndicator(스크롤링 표시기라고도 함). 화면 오른쪽에 있는 표시기로, 개발자가 전달한 상태 객체 유형에 따라 현재 표시기 위치를 표시합니다. 여기서는 ScalingLazyColumnState입니다.

이제 어떤 모습인지 살펴보겠습니다.

cfcbd3003744a6d.png

위아래로 스크롤해 보세요. 스크롤할 때만 스크롤 표시기가 표시되는 것을 알 수 있습니다.

잘하셨습니다. 대부분의 Wear OS 컴포저블의 UI 데모를 마쳤습니다.

9. 축하합니다

축하합니다. Wear OS에서 Compose를 사용하는 방법에 관한 기본사항을 살펴보았습니다.

이제 모든 Compose 지식을 다시 적용하며 멋진 Wear OS 앱을 만들어 보세요.

다음 단계

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

추가 자료

의견

Wear OS용 Compose의 사용 경험과 이를 사용하여 만들 수 있는 기능에 관한 의견을 들려주세요. Kotlin Slack #compose-wear 채널에서 토론에 참여하고 Issue Tracker에 계속 의견을 보내주세요.

즐겁게 코딩해 보세요.