Wear OS용 Compose에서 UI 미리보기

Android 스튜디오 Compose 미리보기를 사용하면 실제 시계나 에뮬레이터에 앱을 배포하지 않고도 IDE에서 직접 다양한 시계 디스플레이 크기, 원형 베젤, 글꼴 크기에 걸쳐 Wear OS 컴포저블을 검사하고 확인할 수 있습니다.

Wear OS 기기는 모서리에서 콘텐츠가 잘리고 TimeTextScrollIndicator와 같은 시스템 오버레이가 화면 가장자리를 따라 곡선으로 표시되는 원형 디스플레이를 사용하므로 Wear OS용 미리보기를 특별히 구성하여 레이아웃 문제를 조기에 포착하는 것이 중요합니다.


미리보기 종속 항목 설정

Wear OS Compose 미리보기 주석과 기기 정의를 사용하려면 모듈의 build.gradle.kts 파일에 다음 종속 항목을 추가하세요.

dependencies {
    // Provides @WearPreview* multipreview annotations
    // (such as @WearPreviewDevices and @WearPreviewFontScales)
    implementation("androidx.wear.compose:compose-ui-tooling:1.7.0")

    // Provides WearDevices constants
    // (such as WearDevices.SMALL_ROUND and WearDevices.LARGE_ROUND)
    implementation("androidx.wear:wear-tooling-preview:1.0.0")

    // Standard Compose preview support and interactive/animation inspection
    implementation("androidx.compose.ui:ui-tooling-preview")
    debugImplementation("androidx.compose.ui:ui-tooling")
}

화면과 구성요소 중 미리 볼 항목 선택

미리보기를 구성하는 방법은 전체 화면을 미리보기하는지 격리된 UI 구성요소를 미리보기하는지에 따라 다릅니다.

전체 화면 미리보기 (AppScaffold + ScreenScaffold)

전체 화면을 미리 볼 때는 항상 Wear 기기 미리보기 주석을 사용하여 화면 컴포저블을 AppScaffoldScreenScaffold로 래핑하세요. 이렇게 하면 원형 시계 디스플레이가 렌더링되고 다음이 보장됩니다.

  • TimeText는 시계 페이스의 상단 곡선 가장자리에 렌더링됩니다.
  • ScrollIndicator이 오른쪽 베젤을 따라 표시됩니다.
  • EdgeButton이 올바르게 배치되고 하단 곡선에서 잘립니다.
  • 콘텐츠 패딩과 원형 화면 클리핑은 실제 시계 하드웨어를 정확하게 반영합니다.
@WearPreviewDevices
@Composable
fun WorkoutScreenPreview() {
    MaterialTheme {
        // AppScaffold provides the top-level TimeText overlay
        AppScaffold {
            // WorkoutScreen contains its own ScreenScaffold and content
            WorkoutScreen(
                heartRate = 142,
                elapsedTime = "12:45"
            )
        }
    }
}
WearDevices.SMALL_ROUND에서 렌더링된 WorkoutScreenPreview

작은 원형 (192x192dp)

WearDevices.LARGE_ROUND에 렌더링된 WorkoutScreenPreview

Large Round (227x227dp)

격리된 구성요소 미리보기

맞춤 Card, Button 또는 상태 칩과 같은 개별 구성요소를 미리 볼 때는 device 매개변수를 생략하고 어두운 배경이 있는 표준 @Preview를 사용합니다. 이렇게 하면 전체 원형 시계 디스플레이를 렌더링하지 않고도 Wear Material 3 색상과 대비가 정확하게 표시됩니다.

@Preview(
    showBackground = true,
    backgroundColor = 0xFF000000
)
@Composable
fun HeartRateCardPreview() {
    MaterialTheme {
        HeartRateCard(bpm = 142, zone = "Aerobic")
    }
}
시계 프레임이 없는 HeartRateCardPreview 격리된 구성요소 미리보기

분리된 구성요소 미리보기 (기기 프레임 없음)


기본 제공 다중 미리보기 주석

androidx.wear.compose.ui.tooling.preview 패키지는 어두운 배경(backgroundColor = 0xFF000000, showBackground = true)과 원형 시계 기기 크기를 자동으로 구성하는 기본 제공 주석을 제공합니다.

주석 렌더링되는 항목 사용하기 적합한 경우
@WearPreviewSmallRound WearDevices.SMALL_ROUND의 미리보기 1개 (192x192dp) 가장 제한적인 원형 디스플레이 크기에 대한 빠른 반복
@WearPreviewLargeRound WearDevices.LARGE_ROUND (227x227dp)에 1개의 미리보기 대형 시계에서 레이아웃 밀도와 추가 간격을 검사합니다.
@WearPreviewDevices 2개의 미리보기: SMALL_ROUNDLARGE_ROUND 모든 화면 컴포저블의 표준 멀티 디바이스 확인
@WearPreviewFontScales 모든 Wear 글꼴 크기에 걸쳐 SMALL_ROUND6개의 미리보기: Small (0.94f), Normal (1.0f), Medium (1.06f), Large (1.12f), Larger (1.18f), Largest (1.24f) 텍스트 줄바꿈, 줄임표, 버튼 높이 확장을 확인합니다.

동일한 미리보기 함수에 @WearPreviewDevices@WearPreviewFontScales를 스택하여 포괄적인 테스트 매트릭스를 생성할 수 있습니다.

@WearPreviewDevices
@WearPreviewFontScales
@Composable
fun MessageDetailScreenPreview() {
    MaterialTheme {
        AppScaffold {
            MessageDetailScreen(
                sender = "Alex",
                body = "Running 5 mins late!"
            )
        }
    }
}

맞춤 미리보기 주석 및 하드웨어 사양

특정 하드웨어 크기, 긴 현지화된 문자열 또는 최악의 조합을 테스트하는 등 더 세밀하게 제어해야 하는 경우 @Preview를 직접 구성하거나 자체 맞춤 다중 미리보기 주석을 정의할 수 있습니다.

사용 가능한 WearDevices 상수 및 맞춤 하드웨어 사양

androidx.wear.tooling.preview.devices.WearDevices 객체는 표준 기기 ID를 제공합니다.

  • WearDevices.SMALL_ROUND ("id:wearos_small_round", 192x192dp)
  • WearDevices.LARGE_ROUND ("id:wearos_large_round", 227x227dp)

대형 원형 디스플레이 (예: 44~45mm 시계 또는 240x240dp의 Ultra 모델)에서 미리보려면 맞춤 spec: 문자열을 device 매개변수에 전달하세요.

@Preview(
    name = "XL Round Watch (240dp)",
    device = "spec:width=240dp,height=240dp,dpi=320,isRound=true",
    showBackground = true,
    backgroundColor = 0xFF000000
)
@Composable
fun WorkoutScreenXlPreview() {
    MaterialTheme {
        AppScaffold {
            WorkoutScreen(heartRate = 142, elapsedTime = "12:45")
        }
    }
}

맞춤 다중 미리보기 주석 만들기

극단적인 시나리오를 검사하려면 가장 작은 원형 화면가장 큰 글꼴 크기 및 자세한 언어 (예: 독일어)와 함께 표준 대형 원형 화면과 페어링하는 맞춤 다중 미리보기 주석을 만드세요.

@Preview(
    name = "1. Standard Large Round",
    group = "Layout extremes",
    device = WearDevices.LARGE_ROUND,
    backgroundColor = 0xFF000000,
    showBackground = true
)
@Preview(
    name = "2. Extreme Small Round (Largest Font + German)",
    group = "Layout extremes",
    device = WearDevices.SMALL_ROUND,
    fontScale = 1.24f,
    locale = "de-rDE",
    backgroundColor = 0xFF000000,
    showBackground = true
)
annotation class WearPreviewExtremes
표준 대형 라운드 미리보기

1. 스탠더드 큰 원형

가장 큰 글꼴 크기의 매우 작은 원형

2. 매우 작은 원형 (가장 큰 글꼴 + 독일어)


스크롤 열 미리보기 (TransformingLazyColumn)

기본적으로 TransformingLazyColumn는 화면 상단에 고정된 첫 번째 항목(index = 0)으로 초기화됩니다. 하지만 Wear OS에서는 항목이 화면의 상단과 하단 곡선 가장자리에 접근할 때 높이와 둥근 모서리 (SurfaceTransformation)가 변형되고 EdgeButton는 하단으로 스크롤할 때만 표시됩니다.

목록을 중간까지 또는 하단까지 스크롤했을 때 목록이 어떻게 표시되는지 미리 보려면 다음 단계를 따르세요.

1단계: 화면 컴포저블에서 TransformingLazyColumnState 호이스팅

화면 컴포저블이 rememberTransformingLazyColumnState()을 기본값으로 사용하는 TransformingLazyColumnState 매개변수를 허용하도록 합니다.

@Composable
fun InboxScreen(
    messages: List<Message>,
    columnState: TransformingLazyColumnState = rememberTransformingLazyColumnState(),
) {
    val transformationSpec = rememberTransformationSpec()

    ScreenScaffold(
        scrollState = columnState,
        edgeButton = {
            EdgeButton(onClick = { /* Compose new */ }) {
                Text("New message")
            }
        }
    ) { contentPadding ->
        TransformingLazyColumn(
            state = columnState,
            contentPadding = contentPadding,
        ) {
            items(messages.size) { index ->
                Card(
                    onClick = {},
                    modifier = Modifier
                        .fillMaxWidth()
                        .transformedHeight(this, transformationSpec)
                        .minimumVerticalContentPadding(
                            CardDefaults.minimumVerticalListContentPadding
                        ),
                    transformation = SurfaceTransformation(transformationSpec),
                ) {
                    Text(messages[index].subject)
                }
            }
        }
    }
}

2단계: @Preview에서 initialAnchorItemIndex 전달

rememberTransformingLazyColumnState는 다음 두 가지 선택적 초기 스크롤 매개변수를 허용합니다.

  • initialAnchorItemIndex: Int: 음수가 아닌 색인(예: 3)으로 설정하면 목록이 해당 항목으로 초기화되며 시계 뷰포트의 중앙에 배치됩니다.
  • initialAnchorItemScrollOffset: Int: 중앙에 배치된 앵커 항목을 기준으로 적용되는 선택적 픽셀 오프셋입니다.

상단, 중간(스크롤됨), 하단 (EdgeButton 표시) 상태를 나란히 표시하는 미리보기를 만들 수 있습니다.

@WearPreviewLargeRound
@Composable
fun InboxScreenTopPreview() {
    MaterialTheme {
        AppScaffold {
            // Default (-1): Pinned to top of list (index 0)
            InboxScreen(messages = sampleMessages)
        }
    }
}

@WearPreviewLargeRound
@Composable
fun InboxScreenScrolledMiddlePreview() {
    MaterialTheme {
        AppScaffold {
            // Centers item index 3 in the viewport, showing top/bottom item morphing
            InboxScreen(
                messages = sampleMessages,
                columnState = rememberTransformingLazyColumnState(
                    initialAnchorItemIndex = 3
                )
            )
        }
    }
}

@WearPreviewLargeRound
@Composable
fun InboxScreenBottomEdgeButtonPreview() {
    MaterialTheme {
        AppScaffold {
            // Anchors on the last item so the EdgeButton is visible at the bottom
            InboxScreen(
                messages = sampleMessages,
                columnState = rememberTransformingLazyColumnState(
                    initialAnchorItemIndex = sampleMessages.lastIndex
                )
            )
        }
    }
}
InboxScreen이 목록 상단에 고정됨

상단 (기본값 -1)

InboxScreen이 중간 색인 3으로 스크롤됨

중간 (initialAnchorItemIndex = 3)

EdgeButton이 펼쳐진 상태로 하단까지 스크롤된 InboxScreen

하단 (EdgeButton 확장)

팁: Android 스튜디오의 @Preview에서 Start Interactive Mode를 클릭하여 마우스나 트랙패드로 TransformingLazyColumn 라이브를 스크롤하고 SurfaceTransformation 모핑, EdgeButton 진입 애니메이션, ScrollIndicator 움직임을 실시간으로 검사할 수도 있습니다.

스크롤 캡처 (LocalScrollCaptureInProgress) 중에 ScrollIndicator 보호

시스템 스크롤 캡처 (긴 스크린샷) 또는 다중 프레임 스크린샷 테스트 도구가 스크롤되는 TransformingLazyColumn를 캡처하면 Compose는 여러 뷰포트 타일을 세로로 캡처하고 스티칭하는 동안 LocalScrollCaptureInProgress.currenttrue로 설정합니다.

ScreenScaffold는 스크롤 캡처 중에 scrollIndicator를 자동으로 숨기지 않으므로 !LocalScrollCaptureInProgress.current로 명시적으로 보호하지 않는 한 긴 스크린샷의 스티치된 모든 타일에 부동 스크롤바 오버레이가 반복되어 표시됩니다.

ScreenScaffold(
    scrollState = columnState,
    scrollIndicator = {
        if (!LocalScrollCaptureInProgress.current) {
            ScrollIndicator(state = columnState)
        }
    }
) { contentPadding ->
    // TransformingLazyColumn content...
    // ...
}