Created
June 9, 2020 04:19
-
-
Save matsushitak/98be509fb1fa74dcc73e278085c87964 to your computer and use it in GitHub Desktop.
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
import androidx.recyclerview.widget.GridLayoutManager | |
import androidx.recyclerview.widget.LinearLayoutManager | |
import androidx.recyclerview.widget.RecyclerView | |
import androidx.recyclerview.widget.StaggeredGridLayoutManager | |
/** | |
* RecyclerViewのページングリスナー | |
*/ | |
// 参考:https://thetopsites.net/article/50618951.shtml | |
abstract class RecyclerViewPagingListener( | |
layoutManager: RecyclerView.LayoutManager, | |
direction: LoadOnScrollDirection | |
) : RecyclerView.OnScrollListener() { | |
/** | |
* 現在読み込んだページ数 | |
*/ | |
private var pendingCurrentPage = 0 | |
/** | |
* 現在読み込んだアイテム数 | |
*/ | |
private var pendingTotalItemCount = 0 | |
/** | |
* ロード中フラグ | |
*/ | |
private var loading = true | |
/** | |
* 検知する方向 | |
*/ | |
private val pendingDirection: LoadOnScrollDirection | |
/** | |
* LinearLayoutManager | |
*/ | |
private var pendingLinearLayoutManager: LinearLayoutManager? = null | |
/** | |
* GridLayoutManager | |
*/ | |
private var pendingGridLayoutManager: GridLayoutManager? = null | |
/** | |
* StaggeredGridLayoutManager | |
*/ | |
private var pendingStaggeredGridLayoutManager: StaggeredGridLayoutManager? = null | |
init { | |
// パフォーマンスのためonScrolledでキャストしない | |
when (layoutManager) { | |
is LinearLayoutManager -> { | |
pendingLinearLayoutManager = layoutManager | |
} | |
is GridLayoutManager -> { | |
pendingGridLayoutManager = layoutManager | |
} | |
is StaggeredGridLayoutManager -> { | |
pendingStaggeredGridLayoutManager = layoutManager | |
} | |
else -> { | |
throw IllegalArgumentException("unsupported this layout manager.") | |
} | |
} | |
pendingDirection = direction | |
} | |
override fun onScrolled(view: RecyclerView, dx: Int, dy: Int) { | |
var lastVisibleItemPosition = 0 | |
var firstVisibleItemPosition = 0 | |
var totalItemCount = 0 | |
when { | |
pendingLinearLayoutManager != null -> { | |
val layoutManager = requireNotNull(pendingLinearLayoutManager) | |
totalItemCount = layoutManager.itemCount | |
lastVisibleItemPosition = layoutManager.findLastVisibleItemPosition() | |
firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition() | |
} | |
pendingGridLayoutManager != null -> { | |
val layoutManager = requireNotNull(pendingGridLayoutManager) | |
totalItemCount = layoutManager.itemCount | |
lastVisibleItemPosition = layoutManager.findLastVisibleItemPosition() | |
firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition() | |
} | |
pendingStaggeredGridLayoutManager != null -> { | |
val layoutManager = requireNotNull(pendingStaggeredGridLayoutManager) | |
val lastVisibleItemPositions = layoutManager.findLastVisibleItemPositions(null) | |
val firstVisibleItemPositions = layoutManager.findFirstVisibleItemPositions(null) | |
totalItemCount = layoutManager.itemCount | |
lastVisibleItemPosition = getLastVisibleItem(lastVisibleItemPositions) | |
firstVisibleItemPosition = getFirstVisibleItem(firstVisibleItemPositions) | |
} | |
} | |
when (pendingDirection) { | |
LoadOnScrollDirection.BOTTOM -> { | |
if (totalItemCount < pendingTotalItemCount) { | |
pendingCurrentPage = STARTING_PAGE_INDEX | |
pendingTotalItemCount = totalItemCount | |
if (totalItemCount == 0) { | |
loading = true | |
} | |
} | |
if (loading && totalItemCount > pendingTotalItemCount) { | |
loading = false | |
pendingTotalItemCount = totalItemCount | |
} | |
if (!loading && lastVisibleItemPosition + PREFETCH_ITEM_POSITION > totalItemCount) { | |
pendingCurrentPage++ | |
onLoadMore(pendingCurrentPage, totalItemCount) | |
loading = true | |
} | |
} | |
LoadOnScrollDirection.TOP -> { | |
if (totalItemCount < pendingTotalItemCount) { | |
pendingCurrentPage = STARTING_PAGE_INDEX | |
pendingTotalItemCount = totalItemCount | |
if (totalItemCount == 0) { | |
loading = true | |
} | |
} | |
if (loading && totalItemCount > pendingTotalItemCount) { | |
loading = false | |
pendingTotalItemCount = totalItemCount | |
} | |
if (!loading && firstVisibleItemPosition < PREFETCH_ITEM_POSITION) { | |
pendingCurrentPage++ | |
onLoadMore(pendingCurrentPage, totalItemCount) | |
loading = true | |
} | |
} | |
} | |
} | |
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 | |
} | |
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 | |
} | |
/** | |
* 追加読み込み | |
* | |
* @param page 現在のページ数 | |
* @param totalItemsCount 現在のアイテム数 | |
*/ | |
abstract fun onLoadMore(page: Int, totalItemsCount: Int) | |
/** | |
* スクロール検知方向 | |
*/ | |
enum class LoadOnScrollDirection { | |
TOP, BOTTOM | |
} | |
companion object { | |
/** | |
* 開始ページ | |
*/ | |
private const val STARTING_PAGE_INDEX = 0 | |
/** | |
* プリフェッチする位置 | |
* | |
* "5"に設定した場合 | |
* "100"アイテムあると"95"が見えた時にロード開始する | |
*/ | |
private const val PREFETCH_ITEM_POSITION = 10 | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment