Пейджер в режиме создания сообщения

Для горизонтального или вертикального перелистывания контента можно использовать составные элементы HorizontalPager и VerticalPager . Они выполняют функции, аналогичные ViewPager в системе представлений. По умолчанию HorizontalPager занимает всю ширину экрана, а VerticalPager — всю высоту. Кроме того, элементы перелистывают только одну страницу за раз. Все эти параметры по умолчанию можно настроить.

HorizontalPager

Для создания пейджера, прокручивающегося горизонтально влево и вправо, используйте HorizontalPager :

Рисунок 1. Демонстрация HorizontalPager .

// Display 10 items
val pagerState = rememberPagerState(pageCount = {
    10
})
HorizontalPager(state = pagerState) { page ->
    // Our page content
    Text(
        text = "Page: $page",
        modifier = Modifier.fillMaxWidth()
    )
}

Вертикальный Пейджер

Для создания пейджера с прокруткой вверх и вниз используйте VerticalPager :

Рисунок 2. Демонстрация 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)
        )
    }
}

На пейджере под содержимым отображается круговой индикатор.
Рисунок 3. Пейджер, на котором под содержимым отображается круговой индикатор.

Примените эффекты прокрутки к содержимому.

Один из распространенных вариантов использования — применение эффектов к элементам пейджера с помощью положения прокрутки. Чтобы узнать, насколько далеко страница находится от выбранной страницы, можно использовать PagerState.currentPageOffsetFraction . Затем можно применять эффекты трансформации к содержимому в зависимости от расстояния до выбранной страницы.

Рисунок 4. Применение преобразований к содержимому Pager.

Например, чтобы отрегулировать прозрачность элементов в зависимости от их расстояния до центра, измените 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 отступа выравнивает страницы по направлению к концу:

Печатная строка с отступами в начале, отображающая содержимое, выровненное по концу.
Рисунок 5. Пейджер с начальным заполнением.

val pagerState = rememberPagerState(pageCount = {
    4
})
HorizontalPager(
    state = pagerState,
    contentPadding = PaddingValues(start = 64.dp),
) { page ->
    // page content
}

Установка одинакового значения start и end отступов центрирует элемент по горизонтали:

Печатная панель с отступами в начале и конце, отображающая содержимое по центру.
Рисунок 6. Пейджер с горизонтальной подкладкой.

val pagerState = rememberPagerState(pageCount = {
    4
})
HorizontalPager(
    state = pagerState,
    contentPadding = PaddingValues(horizontal = 32.dp),
) { page ->
    // page content
}

Установка отступа end страницы выравнивает её по отношению к началу:

Пейджер с отступами в начале и конце, отображающий содержимое, выровненное по началу.
Рисунок 7. Пейджер с торцевой подкладкой.

val pagerState = rememberPagerState(pageCount = {
    4
})
HorizontalPager(
    state = pagerState,
    contentPadding = PaddingValues(end = 64.dp),
) { page ->
    // page content
}

Для достижения аналогичных эффектов в VerticalPager можно задать значения top и bottom . Значение 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 определяет, какой круг будет отображаться в темно-сером цвете.

Результат

В этом видеоролике показана базовая функция автоматической перемотки страниц, представленная в предыдущих фрагментах:

Рисунок 8. Пейджер с автоматической перемоткой страниц, с двухсекундной задержкой между переходами на следующую страницу.

Дополнительные ресурсы

{% verbatim %} {% endverbatim %}