Skip to content

Instantly share code, notes, and snippets.

@rafsanahmad
Last active January 13, 2022 07:19
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rafsanahmad/00214d0f2879884513f8e086754a22e7 to your computer and use it in GitHub Desktop.
Save rafsanahmad/00214d0f2879884513f8e086754a22e7 to your computer and use it in GitHub Desktop.
Smooth infinite scroll for Recyclerview for different layout managers, Kotlin Implementation
abstract class EndlessRecyclerOnScrollListener(
private val threshHold: Int = QUERY_PER_PAGE
) : RecyclerView.OnScrollListener() {
var isError = false
var isLoading = false
var isLastPage = false
var isScrolling = false
private var firstVisibleItem: Int = 0
private var visibleItemCount: Int = 0
private var totalItemCount: Int = 0
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
super.onScrollStateChanged(recyclerView, newState)
if (newState == AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
isScrolling = true
}
}
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
val mLayoutManager = recyclerView.layoutManager
when (mLayoutManager) {
is StaggeredGridLayoutManager -> {
val firstVisibleItemPositions =
mLayoutManager.findFirstVisibleItemPositions(
null
)
// get maximum element within the list
firstVisibleItem = getFirstVisibleItem(firstVisibleItemPositions)
//firstVisibleItem = firstVisibleItemPositions[0]
}
is GridLayoutManager -> {
firstVisibleItem =
mLayoutManager.findFirstVisibleItemPosition()
}
is LinearLayoutManager -> {
firstVisibleItem =
mLayoutManager.findFirstVisibleItemPosition()
}
else -> {
Exception("Unsupported LayoutManager")
}
}
mLayoutManager?.let { manager ->
visibleItemCount = manager.childCount
totalItemCount = manager.itemCount
}
val isNoError = !isError
val isNotLoadingAndIsNotLastPage = !isLoading && !isLastPage
val isAtLastItem = (firstVisibleItem + visibleItemCount) >= totalItemCount
val isNotAtBeginning = firstVisibleItem > 0
val isTotalMoreThanVisible = totalItemCount >= threshHold
val shouldPaginate =
isNoError && isNotLoadingAndIsNotLastPage && isAtLastItem && isNotAtBeginning
&& isTotalMoreThanVisible && isScrolling
if (shouldPaginate) {
isScrolling = false;
onLoadMore()
}
}
private fun getFirstVisibleItem(firstVisibleItemPositions: IntArray): Int {
var maxSize = 0
for (i in firstVisibleItemPositions.indices) {
if (i == 0) {
maxSize = firstVisibleItemPositions[i]
} else if (firstVisibleItemPositions[i] > maxSize) {
maxSize = firstVisibleItemPositions[i]
}
}
return maxSize
}
fun resetOnLoadMore() {
firstVisibleItem = 0
visibleItemCount = 0
totalItemCount = 0
isLoading = true
}
abstract fun onLoadMore()
}
//Use the utility funtion like:
private lateinit var onScrollListener: EndlessRecyclerOnScrollListener
private val QUERY_PER_PAGE = 20 //Page size
// scroll listener for recycler view
onScrollListener = object : EndlessRecyclerOnScrollListener(QUERY_PER_PAGE) {
override fun onLoadMore() {
//Page reaches end
//Call your api
}
}
//add onScrollListener to the Recyclerview
binding.recyclerview.apply {
adapter = RecyclerviewAdapter()
layoutManager = rLayoutManager
addOnScrollListener(onScrollListener)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment