Что такое «бесконечный» пейджер?
«Бесконечный» пейджер (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





