Ответ
Однажды мне нужно было реализовать бесконечную ленту с пагинацией, но с "умной" подгрузкой - чтобы при быстром скролле не грузились промежуточные страницы.
Решение:
val scrollListener = object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
val lastVisiblePosition = layoutManager.findLastVisibleItemPosition()
val threshold = 5
if (lastVisiblePosition > adapter.itemCount - threshold
&& !isLoading
&& !isLastPage) {
loadNextPage()
}
}
}
Ключевые моменты:
- Дебаунс для быстрого скролла
- Проверка
isLoadingиisLastPage - Оптимальный threshold для плавности
- Отмена pending запросов при скролле
Это дало плавный UX и экономию трафика.
Ответ 18+ 🔞
А, слушай, вот тут история про бесконечную ленту — классика, ёпта. Все её делают, а потом охуевают, когда приложение начинает жрать трафик как не в себя и тормозить, будто на дворе 2002-й год.
В общем, надо было мне запилить эту самую подгрузку, но не тупую, которая каждую страницу хватает, а с мозгами. Чтобы пользователь листал как угорелый, а приложение не тупило и не пыталось загрузить всё, что видит. Подозрение ебать чувствую к таким задачам.
Вот смотри, что я придумал. Беру обычный слушатель скролла у RecyclerView и начинаю колдовать.
val scrollListener = object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
val lastVisiblePosition = layoutManager.findLastVisibleItemPosition()
val threshold = 5
if (lastVisiblePosition > adapter.itemCount - threshold
&& !isLoading
&& !isLastPage) {
loadNextPage()
}
}
}
Суть в чём? Мы не ждём, когда юзер доползёт до самого конца списка — это выглядит как говно. Мы начинаем грузить заранее, за несколько элементов до конца. Этот threshold — наша хитрая жопа. Поставишь мало — будет подгрузка дерганая, поставишь много — начнёшь грузить овердохуища лишних данных, пока пользователь просто проматывает.
Но главный фокус — это проверки !isLoading и !isLastPage. Без них — пизда рулю. Представь: запрос на следующую страницу уже полетел, а юзер продолжает скроллить. Если не отслеживать isLoading, ты запустишь ещё пять таких же запросов, они все улетят, а потом начнут возвращаться в разнобой, и интерфейс накроется медным тазом. А isLastPage — чтобы не пытаться грузить страницы от безысходности, когда их уже нет. Доверия ебать ноль к бэкенду, что он всегда корректно скажет "всё, конец".
И ещё одна важная фишка — дебаунс для быстрого скролла. Это когда пользователь резко дёргает ленту вниз. Без дебаунса твой слушатель будет срабатывать каждые полпикселя и пытаться грузить страницы, которые пользователь уже проскроллил мимо. Получается манда с ушами: трафик потрачен, данные загружены, а юзер их даже не увидел. Нужно отменять pending-запросы, как только начался новый скролл к следующей потенциальной точке подгрузки.
В итоге, когда всё настроил, получается красота: лента листается плавно, новые данные подгружаются незаметно, трафик экономится. Сам от себя охуел, что такая, казалось бы, простая штука требует столько подводных камней. Но когда работает — красота, чих-пых тебя в сраку.