콘텐츠를 좌우 또는 상하로 넘기려면 각각 HorizontalPager
및 VerticalPager
컴포저블을 사용하면 됩니다. 이러한 컴포저블은 뷰 시스템의 ViewPager
과 비슷한 기능을 합니다. 기본적으로 HorizontalPager
는 화면의 전체 너비를 차지하고 VerticalPager
는 전체 높이를 차지하며 페이저는 한 번에 한 페이지씩만 플링합니다. 이러한 기본값은 모두 구성할 수 있습니다.
HorizontalPager
좌우로 스크롤되는 페이지로 나누기를 만들려면 HorizontalPager
를 사용하세요.
HorizontalPager
데모
// Display 10 items val pagerState = rememberPagerState(pageCount = { 10 }) HorizontalPager(state = pagerState) { page -> // Our page content Text( text = "Page: $page", modifier = Modifier.fillMaxWidth() ) }
VerticalPager
위아래로 스크롤되는 페이지를 만들려면 VerticalPager
를 사용합니다.
VerticalPager
데모
// Display 10 items val pagerState = rememberPagerState(pageCount = { 10 }) VerticalPager(state = pagerState) { page -> // Our page content Text( text = "Page: $page", modifier = Modifier.fillMaxWidth() ) }
지연 생성
HorizontalPager
및 VerticalPager
의 페이지는 필요할 때 지연 구성되고 배치됩니다. 사용자가 페이지를 스크롤하면 컴포저블은 더 이상 필요하지 않은 페이지를 삭제합니다.
화면 밖에서 페이지 더 로드
기본적으로 페이저는 화면에 표시되는 페이지만 로드합니다. 화면 밖에서 더 많은 페이지를 로드하려면 beyondBoundsPageCount
을 0보다 큰 값으로 설정합니다.
페이지로 나누기에서 항목으로 스크롤
페이지로 이동하려면 rememberPagerState()
를 사용하여 PagerState
객체를 만들고 페이지로 객체를 state
매개변수로 전달합니다. CoroutineScope
내에서 이 상태에 PagerState#scrollToPage()
를 호출할 수 있습니다.
val pagerState = rememberPagerState(pageCount = { 10 }) HorizontalPager(state = pagerState) { page -> // Our page content Text( text = "Page: $page", modifier = Modifier .fillMaxWidth() .height(100.dp) ) } // scroll to page val coroutineScope = rememberCoroutineScope() Button(onClick = { coroutineScope.launch { // Call scroll to on pagerState pagerState.scrollToPage(5) } }, modifier = Modifier.align(Alignment.BottomCenter)) { Text("Jump to Page 5") }
페이지로 애니메이션을 적용하려면 PagerState#animateScrollToPage()
함수를 사용합니다.
val pagerState = rememberPagerState(pageCount = { 10 }) HorizontalPager(state = pagerState) { page -> // Our page content Text( text = "Page: $page", modifier = Modifier .fillMaxWidth() .height(100.dp) ) } // scroll to page val coroutineScope = rememberCoroutineScope() Button(onClick = { coroutineScope.launch { // Call scroll to on pagerState pagerState.animateScrollToPage(5) } }, modifier = Modifier.align(Alignment.BottomCenter)) { Text("Jump to Page 5") }
페이지 상태 변경에 대한 알림 받기
PagerState
에는 페이지에 관한 정보가 포함된 currentPage
, settledPage
, targetPage
의 세 가지 속성이 있습니다.
currentPage
: 스냅 위치에 가장 가까운 페이지입니다. 기본적으로 스냅 위치는 레이아웃의 시작 부분에 있습니다.settledPage
: 애니메이션이나 스크롤이 실행되지 않을 때의 페이지 번호입니다. 이는currentPage
속성과 다릅니다.currentPage
는 페이지가 스냅 위치에 충분히 가까우면 즉시 업데이트되지만settledPage
는 모든 애니메이션이 완료될 때까지 동일하게 유지됩니다.targetPage
: 스크롤 이동의 제안된 중지 위치입니다.
snapshotFlow
함수를 사용하여 이러한 변수의 변경사항을 관찰하고 이에 반응할 수 있습니다. 예를 들어 각 페이지 변경 시 애널리틱스 이벤트를 전송하려면 다음을 실행하면 됩니다.
val pagerState = rememberPagerState(pageCount = { 10 }) LaunchedEffect(pagerState) { // Collect from the a snapshotFlow reading the currentPage snapshotFlow { pagerState.currentPage }.collect { page -> // Do something with each page change, for example: // viewModel.sendPageSelectedEvent(page) Log.d("Page change", "Page changed to $page") } } VerticalPager( state = pagerState, ) { page -> Text(text = "Page: $page") }
페이지 표시기 추가
페이지에 표시기를 추가하려면 PagerState
객체를 사용하여 페이지 수 중에서 선택된 페이지에 관한 정보를 가져오고 맞춤 표시기를 그립니다.
예를 들어 간단한 원 표시기를 원하는 경우 원의 개수를 반복하고 페이지가 선택되었는지에 따라 pagerState.currentPage
를 사용하여 원 색상을 변경할 수 있습니다.
val pagerState = rememberPagerState(pageCount = { 4 }) HorizontalPager( state = pagerState, modifier = Modifier.fillMaxSize() ) { page -> // Our page content Text( text = "Page: $page", ) } Row( Modifier .wrapContentHeight() .fillMaxWidth() .align(Alignment.BottomCenter) .padding(bottom = 8.dp), horizontalArrangement = Arrangement.Center ) { repeat(pagerState.pageCount) { iteration -> val color = if (pagerState.currentPage == iteration) Color.DarkGray else Color.LightGray Box( modifier = Modifier .padding(2.dp) .clip(CircleShape) .background(color) .size(16.dp) ) } }

콘텐츠에 항목 스크롤 효과 적용
일반적인 사용 사례는 스크롤 위치를 사용하여 페이지로 나누기 항목에 효과를 적용하는 것입니다. 페이지가 현재 선택된 페이지에서 얼마나 떨어져 있는지 확인하려면 PagerState.currentPageOffsetFraction
을 사용하면 됩니다.
그런 다음 선택한 페이지와의 거리에 따라 콘텐츠에 변환 효과를 적용할 수 있습니다.
예를 들어 항목이 중앙에서 얼마나 떨어져 있는지에 따라 항목의 불투명도를 조정하려면 페이지 표시기 내 항목에서 Modifier.graphicsLayer
을 사용하여 alpha
를 변경합니다.
val pagerState = rememberPagerState(pageCount = { 4 }) HorizontalPager(state = pagerState) { page -> Card( Modifier .size(200.dp) .graphicsLayer { // Calculate the absolute offset for the current page from the // scroll position. We use the absolute value which allows us to mirror // any effects for both directions val pageOffset = ( (pagerState.currentPage - page) + pagerState .currentPageOffsetFraction ).absoluteValue // We animate the alpha, between 50% and 100% alpha = lerp( start = 0.5f, stop = 1f, fraction = 1f - pageOffset.coerceIn(0f, 1f) ) } ) { // Card content } }
맞춤 페이지 크기
기본적으로 HorizontalPager
는 전체 너비를 차지하고 VerticalPager
는 전체 높이를 차지합니다. pageSize
변수를 Fixed
, Fill
(기본값) 또는 맞춤 크기 계산을 갖도록 설정할 수 있습니다.
예를 들어 100.dp
의 고정 너비 페이지를 설정하려면 다음을 실행합니다.
val pagerState = rememberPagerState(pageCount = { 4 }) HorizontalPager( state = pagerState, pageSize = PageSize.Fixed(100.dp) ) { page -> // page content }
표시 영역 크기에 따라 페이지 크기를 조정하려면 맞춤 페이지 크기 계산을 사용하세요. 맞춤 PageSize
객체를 만들고 항목 간 간격을 고려하여 availableSpace
을 3으로 나눕니다.
private val threePagesPerViewport = object : PageSize { override fun Density.calculateMainAxisPageSize( availableSpace: Int, pageSpacing: Int ): Int { return (availableSpace - 2 * pageSpacing) / 3 } }
콘텐츠 패딩
HorizontalPager
과 VerticalPager
는 모두 콘텐츠 패딩 변경을 지원하므로 페이지의 최대 크기와 정렬에 영향을 줄 수 있습니다.
예를 들어 start
패딩을 설정하면 페이지가 끝쪽으로 정렬됩니다.
val pagerState = rememberPagerState(pageCount = { 4 }) HorizontalPager( state = pagerState, contentPadding = PaddingValues(start = 64.dp), ) { page -> // page content }
start
및 end
패딩을 동일한 값으로 설정하면 항목이 가로로 가운데에 배치됩니다.
val pagerState = rememberPagerState(pageCount = { 4 }) HorizontalPager( state = pagerState, contentPadding = PaddingValues(horizontal = 32.dp), ) { page -> // page content }
end
패딩을 설정하면 페이지가 시작 부분으로 정렬됩니다.
val pagerState = rememberPagerState(pageCount = { 4 }) HorizontalPager( state = pagerState, contentPadding = PaddingValues(end = 64.dp), ) { page -> // page content }
top
및 bottom
값을 설정하여 VerticalPager
에 유사한 효과를 적용할 수 있습니다. 여기서 32.dp
값은 예로만 사용됩니다. 패딩 크기를 원하는 값으로 설정할 수 있습니다.
스크롤 동작 맞춤설정
기본 HorizontalPager
및 VerticalPager
컴포저블은 스크롤 동작이 페이저와 어떻게 작동하는지 지정합니다. 하지만 pagerSnapDistance
또는 flingBehavior
과 같은 기본값을 맞춤설정하고 변경할 수 있습니다.
맞추기 거리
기본적으로 HorizontalPager
및 VerticalPager
은 플링 동작이 한 번에 한 페이지씩 스크롤할 수 있는 최대 페이지 수를 설정합니다. 이를 변경하려면 flingBehavior
에서 pagerSnapDistance
을 설정하세요.
val pagerState = rememberPagerState(pageCount = { 10 }) val fling = PagerDefaults.flingBehavior( state = pagerState, pagerSnapDistance = PagerSnapDistance.atMost(10) ) Column(modifier = Modifier.fillMaxSize()) { HorizontalPager( state = pagerState, pageSize = PageSize.Fixed(200.dp), beyondViewportPageCount = 10, flingBehavior = fling ) { PagerSampleItem(page = it) } }
자동 진행 페이지 만들기
이 섹션에서는 Compose에서 페이지 표시기가 있는 자동 진행 페이지를 만드는 방법을 설명합니다. 항목 모음은 자동으로 가로로 스크롤되지만 사용자는 항목 간에 수동으로 스와이프할 수도 있습니다. 사용자가 페이지 표시기와 상호작용하면 자동 진행이 중지됩니다.
기본 예
다음 스니펫은 각 페이지가 다른 색상으로 렌더링되는 시각적 표시기가 있는 기본 자동 진행 페이지 구현을 만듭니다.
@Composable fun AutoAdvancePager(pageItems: List<Color>, modifier: Modifier = Modifier) { Box(modifier = Modifier.fillMaxSize()) { val pagerState = rememberPagerState(pageCount = { pageItems.size }) val pagerIsDragged by pagerState.interactionSource.collectIsDraggedAsState() val pageInteractionSource = remember { MutableInteractionSource() } val pageIsPressed by pageInteractionSource.collectIsPressedAsState() // Stop auto-advancing when pager is dragged or one of the pages is pressed val autoAdvance = !pagerIsDragged && !pageIsPressed if (autoAdvance) { LaunchedEffect(pagerState, pageInteractionSource) { while (true) { delay(2000) val nextPage = (pagerState.currentPage + 1) % pageItems.size pagerState.animateScrollToPage(nextPage) } } } HorizontalPager( state = pagerState ) { page -> Text( text = "Page: $page", textAlign = TextAlign.Center, modifier = modifier .fillMaxSize() .background(pageItems[page]) .clickable( interactionSource = pageInteractionSource, indication = LocalIndication.current ) { // Handle page click } .wrapContentSize(align = Alignment.Center) ) } PagerIndicator(pageItems.size, pagerState.currentPage) } }
코드에 관한 핵심 사항
AutoAdvancePager
함수는 자동 진행을 사용하여 가로로 페이지를 나누는 뷰를 만듭니다. 각 페이지의 배경색으로 사용되는Color
객체 목록을 입력으로 사용합니다.pagerState
는 페이저의 상태를 보유하는rememberPagerState
를 사용하여 생성됩니다.pagerIsDragged
및pageIsPressed
은 사용자 상호작용을 추적합니다.- 사용자가 페이지를 드래그하거나 페이지 중 하나를 누르지 않는 한
LaunchedEffect
는 2초마다 페이지를 자동으로 이동합니다. HorizontalPager
에는 페이지 목록이 표시되며 각 페이지에는 페이지 번호를 표시하는Text
컴포저블이 있습니다. 수정자는 페이지를 채우고,pageItems
에서 배경색을 설정하고, 페이지를 클릭 가능하게 만듭니다.
@Composable fun PagerIndicator(pageCount: Int, currentPageIndex: Int, modifier: Modifier = Modifier) { Box(modifier = Modifier.fillMaxSize()) { Row( modifier = Modifier .wrapContentHeight() .fillMaxWidth() .align(Alignment.BottomCenter) .padding(bottom = 8.dp), horizontalArrangement = Arrangement.Center ) { repeat(pageCount) { iteration -> val color = if (currentPageIndex == iteration) Color.DarkGray else Color.LightGray Box( modifier = modifier .padding(2.dp) .clip(CircleShape) .background(color) .size(16.dp) ) } } } }
코드에 관한 핵심 사항
Box
컴포저블이 루트 요소로 사용됩니다.Box
내에서Row
컴포저블은 페이지 표시기를 가로로 정렬합니다.
- 맞춤 페이지 표시기는 원의 행으로 표시되며, 각 원은
circle
에 클리핑되어 페이지를 나타냅니다.Box
- 현재 페이지의 원은
DarkGray
로 색칠되고 다른 원은LightGray
로 색칠됩니다.currentPageIndex
매개변수는 진한 회색으로 렌더링되는 원을 결정합니다.
결과
이 동영상은 이전 스니펫의 기본 자동 진행 페이지를 보여줍니다.