Для горизонтального или вертикального перелистывания контента можно использовать компонуемые элементы HorizontalPager и VerticalPager . Их функции аналогичны функциям ViewPager в системе представлений. По умолчанию HorizontalPager занимает всю ширину экрана, а VerticalPager — всю высоту. Кроме того, пейджеры перелистывают только одну страницу за раз. Все эти значения по умолчанию можно настроить.
Горизонтальный пейджер
Чтобы создать пейджер, прокручивающийся горизонтально влево и вправо, используйте 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
// 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 больше нуля.
Прокрутите до нужного элемента в пейджере
Чтобы прокрутить страницу на нужную страницу в пейджере, создайте объект PagerState с помощью rememberPagerState() и передайте его в качестве параметра state пейджеру. Вы можете вызвать PagerState#scrollToPage() для этого состояния внутри CoroutineScope :
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 . Затем можно применить эффекты преобразования к контенту в зависимости от расстояния до выбранной страницы.
Например, чтобы настроить непрозрачность элементов в зависимости от их удаленности от центра, измените alpha с помощью Modifier.graphicsLayer для элемента внутри пейджера:
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 на три, учитывая расстояние между элементами:
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 задают максимальное количество страниц, которые можно прокрутить одним жестом, — по одной странице за раз. Чтобы изменить это, задайте pagerSnapDistance для flingBehavior :
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автоматически перелистывает страницы каждые две секунды, если пользователь не перетаскивает страницу или не нажимает одну из страниц. -
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выступает в качестве корневого элемента и содержитRowдля горизонтального расположения индикаторов страниц. - Пользовательский индикатор страницы отображается в виде ряда кругов, где каждый
Box, прикрепленный кCircleShape, представляет собой страницу. - Круг текущей страницы окрашен в
DarkGray, а остальные — вLightGray. ПараметрcurrentPageIndexопределяет, какой круг будет отображаться тёмно-серым.
Результат
В этом видео показана базовая функция автоматической перелистывания страниц из предыдущих фрагментов: