127 lines
4.7 KiB
Kotlin
127 lines
4.7 KiB
Kotlin
package org.moire.ultrasonic.fragment
|
||
|
||
import androidx.recyclerview.widget.GridLayoutManager
|
||
import androidx.recyclerview.widget.LinearLayoutManager
|
||
import androidx.recyclerview.widget.RecyclerView
|
||
import androidx.recyclerview.widget.StaggeredGridLayoutManager
|
||
|
||
/*
|
||
* An abstract ScrollListener, which can be extended to provide endless scrolling capabilities
|
||
*/
|
||
abstract class EndlessScrollListener : RecyclerView.OnScrollListener {
|
||
// The minimum amount of items to have below your current scroll position
|
||
// before loading more.
|
||
private var treshold = VISIBLE_TRESHOLD
|
||
|
||
// The current offset index of data you have loaded
|
||
private var currentPage = 0
|
||
|
||
// The total number of items in the dataset after the last load
|
||
private var previousTotalItemCount = 0
|
||
|
||
// True if we are still waiting for the last set of data to load.
|
||
private var loading = true
|
||
|
||
// Sets the starting page index
|
||
private val startingPageIndex = 0
|
||
var thisManager: RecyclerView.LayoutManager
|
||
|
||
constructor(layoutManager: LinearLayoutManager) {
|
||
thisManager = layoutManager
|
||
}
|
||
|
||
@Suppress("Unused")
|
||
constructor(layoutManager: GridLayoutManager) {
|
||
thisManager = layoutManager
|
||
treshold *= layoutManager.spanCount
|
||
}
|
||
|
||
@Suppress("Unused")
|
||
constructor(layoutManager: StaggeredGridLayoutManager) {
|
||
thisManager = layoutManager
|
||
treshold *= layoutManager.spanCount
|
||
}
|
||
|
||
private fun getLastVisibleItem(lastVisibleItemPositions: IntArray): Int {
|
||
var maxSize = 0
|
||
for (i in lastVisibleItemPositions.indices) {
|
||
if (i == 0) {
|
||
maxSize = lastVisibleItemPositions[i]
|
||
} else if (lastVisibleItemPositions[i] > maxSize) {
|
||
maxSize = lastVisibleItemPositions[i]
|
||
}
|
||
}
|
||
return maxSize
|
||
}
|
||
|
||
// This happens many times a second during a scroll, so be wary of the code you place here.
|
||
// We are given a few useful parameters to help us work out if we need to load some more data,
|
||
// but first we check if we are waiting for the previous load to finish.
|
||
override fun onScrolled(view: RecyclerView, dx: Int, dy: Int) {
|
||
var lastVisibleItemPosition = 0
|
||
|
||
val thisManager: RecyclerView.LayoutManager = thisManager
|
||
val totalItemCount = thisManager.itemCount
|
||
|
||
when (thisManager) {
|
||
is StaggeredGridLayoutManager -> {
|
||
val lastVisibleItemPositions =
|
||
thisManager.findLastVisibleItemPositions(null)
|
||
// get maximum element within the list
|
||
lastVisibleItemPosition = getLastVisibleItem(lastVisibleItemPositions)
|
||
}
|
||
is GridLayoutManager -> {
|
||
lastVisibleItemPosition =
|
||
thisManager.findLastVisibleItemPosition()
|
||
}
|
||
is LinearLayoutManager -> {
|
||
lastVisibleItemPosition =
|
||
thisManager.findLastVisibleItemPosition()
|
||
}
|
||
}
|
||
|
||
// If the total item count is zero and the previous isn't, assume the
|
||
// list is invalidated and should be reset back to initial state
|
||
if (totalItemCount < previousTotalItemCount) {
|
||
currentPage = startingPageIndex
|
||
previousTotalItemCount = totalItemCount
|
||
if (totalItemCount == 0) {
|
||
loading = true
|
||
}
|
||
}
|
||
// If it’s still loading, we check to see if the dataset count has
|
||
// changed, if so we conclude it has finished loading and update the current page
|
||
// number and total item count.
|
||
if (loading && totalItemCount > previousTotalItemCount) {
|
||
loading = false
|
||
previousTotalItemCount = totalItemCount
|
||
}
|
||
|
||
// If it isn’t currently loading, we check to see if we have breached
|
||
// the visibleThreshold and need to reload more data.
|
||
// If we do need to reload some more data, we execute onLoadMore to fetch the data.
|
||
// threshold should reflect how many total columns there are too
|
||
if (!loading && lastVisibleItemPosition + treshold > totalItemCount) {
|
||
currentPage++
|
||
onLoadMore(currentPage, totalItemCount, view)
|
||
loading = true
|
||
}
|
||
}
|
||
|
||
// Call this method whenever performing new searches
|
||
fun resetState() {
|
||
currentPage = startingPageIndex
|
||
previousTotalItemCount = 0
|
||
loading = true
|
||
}
|
||
|
||
// Defines the process for actually loading more data based on page
|
||
abstract fun onLoadMore(page: Int, totalItemsCount: Int, view: RecyclerView?)
|
||
|
||
companion object {
|
||
// The minimum amount of items to have below your current scroll position
|
||
// before loading more.
|
||
const val VISIBLE_TRESHOLD = 7
|
||
}
|
||
}
|