Last active
January 13, 2022 07:19
-
-
Save rafsanahmad/00214d0f2879884513f8e086754a22e7 to your computer and use it in GitHub Desktop.
Smooth infinite scroll for Recyclerview for different layout managers, Kotlin Implementation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//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