Что такое «бесконечный» пейджер?
«Бесконечный» пейджер (endless/infinite pager) — элемент пользовательского интерфейса, создающий иллюзию бесконечного скроллинга за счет непрерывной цикличности просмотра контента. Он часто используется в слайдерах (каруселях) изображений, новостных лентах, галереях товаров — везде, где выгоден непрерывный просмотр.

Как сделать обычный пейджер “бесконечным”
Любой обычный пейджер можно превратить в «бесконечный», то есть пейджер с бесконечным скроллингом, используя простые приемы.
Как это работает
Принцип работы «бесконечного пейджера» заключается в создании иллюзии бесконечного скроллинга. Он достигается с помощью следующих стратегий.
- Расширение диапазона страниц. Умножая общее количество страниц на большую константу, можно создать гораздо больший диапазон страниц. Это гарантирует, что пользователи смогут долгое время прокручивать страницу в обоих направлениях, не доходя до конца.
- Установление начальной страницы в середине диапазона страниц. Если начать с середины, пользователи потратят значительное время на прокрутку, прежде чем доберутся до конца.
- Обертывание индекса. При отображении контента индекс текущей страницы обертывается с помощью оператора модуля (
%
). Это позволяет многократно переходить к контенту независимо от того, насколько далеко прокручивает страницу пользователь.
Описанные стратегии, несмотря на свою простоту, позволяют создать плавную, зацикленную навигацию.
Важное замечание: при выборе большой константы важно выбрать большое число, чтобы создать иллюзию бесконечного скроллинга, но не настолько большое, чтобы вызвать проблемы с производительностью или ошибку ANR (application not responding — приложение не отвечает). Это связано с тем, что Jetpack Compose кэширует страницы, и использование слишком большого числа может привести к увеличению потребления памяти и потенциальному замедлению работы приложения.
Пример кода
Демонстрацию того, как преобразовать обычный пейджер в «бесконечный» с помощью Jetpack Compose, можно посмотреть в репозитории на Github.
Исходный код
Вот исходный код реализации пейджера с помощью Jetpack Compose:
@OptIn(ExperimentalFoundationApi::class) @Composable internal fun AnimatedViewPager( modifier: Modifier = Modifier, pageSize: Dp, @DrawableRes drawables: List<Int>, ) { val pagerState = rememberPagerState( initialPage = 0, initialPageOffsetFraction = 0f, pageCount = { drawables.size }, ) var currentPageIndex by remember { mutableIntStateOf(0) } val hapticFeedback = LocalHapticFeedback.current LaunchedEffect(pagerState) { snapshotFlow { pagerState.currentPage }.collect { currentPage -> if (currentPageIndex != currentPage) { hapticFeedback.performHapticFeedback( hapticFeedbackType = HapticFeedbackType.LongPress, ) currentPageIndex = currentPage } } } HorizontalPager( modifier = modifier, state = pagerState, contentPadding = PaddingValues(horizontal = pageSize), verticalAlignment = Alignment.CenterVertically, ) { thisPageIndex -> PageLayout( modifier = Modifier .size(size = pageSize) .pagerAnimation( pagerState = pagerState, thisPageIndex = thisPageIndex, ), drawable = drawables[thisPageIndex], ) } }
Модифицированный код для реализации «бесконечного» пейджинга
Вот изменения, необходимые для преобразования обычного пейджера в «бесконечный»:
@OptIn(ExperimentalFoundationApi::class) @Composable internal fun AnimatedViewPager( modifier: Modifier = Modifier, pageSize: Dp, @DrawableRes drawables: List<Int>, ) { val endlessPagerMultiplier = 1000 val pageCount = endlessPagerMultiplier * drawables.size val initialPage = pageCount / 2 val pagerState = rememberPagerState( initialPage = initialPage, initialPageOffsetFraction = 0f, pageCount = { pageCount }, ) var currentPageIndex by remember { mutableIntStateOf(initialPage) } val hapticFeedback = LocalHapticFeedback.current LaunchedEffect(pagerState) { snapshotFlow { pagerState.currentPage }.collect { currentPage -> if (currentPageIndex != currentPage) { hapticFeedback.performHapticFeedback( hapticFeedbackType = HapticFeedbackType.LongPress, ) currentPageIndex = currentPage } } } HorizontalPager( modifier = modifier, state = pagerState, contentPadding = PaddingValues(horizontal = pageSize), verticalAlignment = Alignment.CenterVertically, ) { absolutePageIndex -> val resolvedPageContentIndex = absolutePageIndex % drawables.size PageLayout( modifier = Modifier .size(size = pageSize) .pagerAnimation( pagerState = pagerState, thisPageIndex = absolutePageIndex, ), drawable = drawables[resolvedPageContentIndex], ) } }
Ключевые изменения в коде
1. Увеличение количества страниц
Умножаем исходное количество страниц на большую константу (endlessPagerMultiplier
) и устанавливаем initialPage
в середину этого расширенного диапазона.
val endlessPagerMultiplier = 1000
val pageCount = endlessPagerMultiplier * drawables.size
val initialPage = pageCount / 2
val pagerState = rememberPagerState(
initialPage = initialPage,
initialPageOffsetFraction = 0f,
pageCount = { pageCount },
)
2. Обертывание индекса
Используем оператор модуля, чтобы обернуть индекс страницы при определении того, какой контент отображать.
val resolvedPageContentIndex = absolutePageIndex % drawables.size
Заключение
Достаточно нескольких строк кода, чтобы превратить стандартный пейджер в «бесконечный». Эти минимальные изменения в коде позволят значительно повысить удобство программного продукта и вовлеченность пользователей.
Читайте также:
- Как создать анимированный переключатель тем в Jetpack Compose
- Эффективная стратегия тестирования Android-проектов. Часть 1
- Новые способы оптимизации стабильности в Jetpack Compose
Читайте нас в Telegram, VK и Дзен
Перевод статьи Ryan W: Implementing an Endless Pager in Jetpack Compose