페이징을 사용하면 사용자가 Wear OS 기기에서 서로 다른 전체 화면 페이지 간에 가로 또는 세로로 스와이프할 수 있습니다. 일반적인 사용 사례로는 운동 앱에서 운동 측정항목과 미디어 컨트롤 간에 스와이프하거나 다중 페이지 흐름을 단계별로 진행하는 것이 있습니다.
Wear OS Material 3용 Compose에서 HorizontalPagerScaffold와 VerticalPagerScaffold는 페이지 표시기를 자동으로 배치하고 TimeText로 전환을 관리하여 페이지 탐색기 레이아웃을 조정합니다.
각 페이지는 AnimatedPage 컴포저블로 래핑됩니다. 이 컴포저블은 페이지가 원형 디스플레이를 통해 전환될 때 크기 조절, 둥근 모서리 모핑, 스크림 효과를 적용합니다.
다음 애니메이션은 가로 스와이프 중에 HorizontalPagerScaffold 및 AnimatedPage이 페이지를 확장하고 애니메이션을 적용하는 방법을 보여줍니다.
페이지로 나누기를 위한 스캐폴드 계층 구조
Material 3에서 페이지로 나뉜 화면 레이아웃을 디자인할 때는 왼쪽이나 오른쪽 (HorizontalPager) 또는 위나 아래 (VerticalPager)로 스와이프하는지 여부에 관계없이 외부 컨테이너에서 내부 콘텐츠까지 다음 구성요소 계층 구조를 사용하세요.
AppScaffold: 앱의 루트에 있는 가장 바깥쪽 컨테이너입니다(앱당 하나만 사용). 페이지 전환 및 스와이프하여 닫기 동작 중에 시계가 화면 상단에 고정되도록 전역TimeText오버레이를 고정합니다.HorizontalPagerScaffold또는VerticalPagerScaffold: 페이지 수준에서AppScaffold내부에 배치됩니다.TimeText와HorizontalPageIndicator또는VerticalPageIndicator간 전환을 조정합니다.HorizontalPager또는VerticalPager: 공유PagerState를 사용하여 스와이프 동작, 플링 물리학, 로터리 입력 맞추기를 관리하는 기본 페이저 컨테이너입니다.AnimatedPage: 페이지로 나누기의 페이지 콘텐츠 람다 내에 배치됩니다.PagerState의 페이지 오프셋을 기반으로 Material 3 전환 애니메이션(스케일링 및 스크림 효과)을 적용하기 위해 각 개별 페이지를 래핑합니다.ScreenScaffold: 각 개별 페이지의AnimatedPage내부에 배치됩니다. 각 페이지에는 자체적으로 세로로 스크롤 가능한 목록 (예:TransformingLazyColumn) 또는 자체EdgeButton이 포함될 수 있으므로 각 페이지 내에ScreenScaffold을 중첩하면 세로 스크롤 표시기, 가장자리 버튼,contentPadding이 해당 페이지에 속하고 페이지와 함께 원활하게 애니메이션 처리됩니다.
AppScaffold (1 per app: anchors global TimeText)
└── HorizontalPagerScaffold / VerticalPagerScaffold (manages PageIndicator)
└── HorizontalPager / VerticalPager (manages PagerState & fling behavior)
└── AnimatedPage (applies scaling & scrim transitions per page)
└── ScreenScaffold (1 per page: ScrollIndicator & EdgeButton)
└── Page Content (Column or TransformingLazyColumn)
수평 페이저 구현
Material 3에서 가로 페이저를 구현하려면 앱의 AppScaffold 내부에 HorizontalPagerScaffold, HorizontalPager, AnimatedPage, ScreenScaffold를 중첩하세요.
flingBehavior에 PagerScaffoldDefaults.snapWithSpringFlingBehavior를 사용하여 Material 3 스프링 모션 물리학 및 반응형 페이지 스냅 (HighSnapPositionalThreshold)을 적용합니다. 기본적으로 HorizontalPager는 로터리 페이지 스크롤 (rotaryScrollableBehavior = null)을 사용 중지하므로 회전하는 크라운 또는 베젤 입력으로 활성 페이지 내에서 세로 목록 (예: TransformingLazyColumn)을 스크롤할 수 있습니다.
다음 샘플은 완전한 HorizontalPagerScaffold 설정을 보여줍니다.
@Composable fun HorizontalPagerScaffoldSample(navigateBack: () -> Unit) { AppScaffold { val pagerState = rememberPagerState(pageCount = { 10 }) HorizontalPagerScaffold(pagerState = pagerState) { HorizontalPager( state = pagerState, flingBehavior = PagerScaffoldDefaults.snapWithSpringFlingBehavior( state = pagerState ), ) { page -> AnimatedPage(pageIndex = page, pagerState = pagerState) { ScreenScaffold { Column( modifier = Modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center, ) { Text(text = "Page #$page") Spacer(modifier = Modifier.height(8.dp)) Text(text = "Swipe left and right") if (page == 0) { Spacer(modifier = Modifier.height(16.dp)) Button(onClick = navigateBack) { Text("Exit") } } } } } } } } }
가로 페이저 내 페이지에 TransformingLazyColumn가 포함된 경우 해당 페이지의 범위 내에 별도의 rememberTransformingLazyColumnState()를 만들어 내부 ScreenScaffold 및 TransformingLazyColumn에 전달합니다.
AppScaffold { val pagerState = rememberPagerState(pageCount = { 10 }) HorizontalPagerScaffold(pagerState = pagerState) { HorizontalPager( state = pagerState, flingBehavior = PagerScaffoldDefaults.snapWithSpringFlingBehavior( state = pagerState ), ) { page -> AnimatedPage(pageIndex = page, pagerState = pagerState) { val columnState = rememberTransformingLazyColumnState() val transformationSpec = rememberTransformationSpec() ScreenScaffold( scrollState = columnState, ) { contentPadding -> TransformingLazyColumn( state = columnState, contentPadding = contentPadding, ) { item { ListHeader( modifier = Modifier .fillMaxWidth() .transformedHeight(this, transformationSpec) .minimumVerticalContentPadding( ListHeaderDefaults.minimumTopListContentPadding ), transformation = SurfaceTransformation(transformationSpec), ) { Text(text = "Pager sample") } } item { Card( modifier = Modifier .fillMaxWidth() .transformedHeight(this, transformationSpec) .minimumVerticalContentPadding( CardDefaults.minimumVerticalListContentPadding ), transformation = SurfaceTransformation(transformationSpec), ) { if (page == 0) { Text(text = "Page #$page. Swipe right") } else { Text(text = "Page #$page. Swipe left and right") } } } } } } } } }
세로 페이저 구현
세로 페이저를 사용하면 사용자가 페이지 간에 위아래로 스와이프하거나 시계의 회전식 측면 버튼이나 베젤을 사용하여 페이지를 단계별로 탐색할 수 있습니다.
HorizontalPager와 달리 VerticalPager는 기본적으로 회전 페이지 스냅을 사용 설정합니다 (RotaryScrollableDefaults.snapBehavior(state)). PagerScaffoldDefaults.snapWithSpringFlingBehavior를 flingBehavior에 전달하고 각 페이지를 AnimatedPage 및 ScreenScaffold로 래핑합니다.
@Composable fun VerticalPagerScaffoldSample() { AppScaffold { val pagerState = rememberPagerState(pageCount = { 10 }) VerticalPagerScaffold(pagerState = pagerState) { VerticalPager( state = pagerState, flingBehavior = PagerScaffoldDefaults.snapWithSpringFlingBehavior( state = pagerState ), ) { page -> AnimatedPage(pageIndex = page, pagerState = pagerState) { ScreenScaffold { Column( modifier = Modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center, ) { Text(text = "Page #$page") Spacer(modifier = Modifier.height(8.dp)) Text(text = "Swipe up and down") } } } } } } }
페이지로 나누기 동작 맞춤설정
앱의 상호작용 요구사항에 맞게 HorizontalPagerScaffold 및 VerticalPagerScaffold의 스냅 감도를 맞춤설정할 수 있습니다.
운동의 스냅 민감도 조정하기
운동이나 사용자의 대근육 운동 제어가 제한되는 움직임이 많은 활동 중에 실수로 터치하거나 용두를 약간 회전하면 의도치 않게 페이지가 전환될 수 있습니다. 이러한 화면의 경우 스냅 감도를 낮게 설정하여 페이지를 구성하세요.
maxFlingPages = 0및snapPositionalThreshold = PagerScaffoldDefaults.LowSnapPositionalThreshold와 함께PagerDefaults.snapFlingBehavior를 전달하므로 페이지를 넘기려면 화면의 더 큰 부분을 의도적으로 드래그해야 합니다.- 로터리 페이지 스냅을 사용 설정하는 경우
RotaryScrollableDefaults.snapBehavior에서snapSensitivity = RotaryScrollableDefaults.LowSnapSensitivity를 설정하여 다음 페이지로 스냅하기 전에 더 많은 회전이 필요하도록 합니다.
@Composable fun HorizontalPagerScaffoldWithLowSensitivitySample(navigateBack: () -> Unit) { AppScaffold { val pagerState = rememberPagerState(pageCount = { 3 }) HorizontalPagerScaffold(pagerState = pagerState) { HorizontalPager( state = pagerState, flingBehavior = PagerDefaults.snapFlingBehavior( state = pagerState, maxFlingPages = 0, snapPositionalThreshold = PagerScaffoldDefaults.LowSnapPositionalThreshold, ), rotaryScrollableBehavior = RotaryScrollableDefaults.snapBehavior( pagerState = pagerState, snapSensitivity = RotaryScrollableDefaults.LowSnapSensitivity, ), ) { page -> AnimatedPage(pageIndex = page, pagerState = pagerState) { ScreenScaffold { // Page content } } } } } }
전체 샘플 구현은 android/snippets 저장소의 Pager.kt과 AndroidX 저장소의 PagerScaffoldSample.kt을 참고하세요.