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
|
|||
|
}
|
|||
|
}
|