Fix webtoon scroll dispatching
This commit is contained in:
@@ -263,7 +263,7 @@ class ReaderViewModel @Inject constructor(
|
||||
}
|
||||
|
||||
@MainThread
|
||||
fun onCurrentPageChanged(position: Int) {
|
||||
fun onCurrentPageChanged(lowerPos: Int, upperPos: Int) {
|
||||
val prevJob = stateChangeJob
|
||||
val pages = content.value.pages // capture immediately
|
||||
stateChangeJob = launchJob(Dispatchers.Default) {
|
||||
@@ -272,7 +272,8 @@ class ReaderViewModel @Inject constructor(
|
||||
if (pages.size != content.value.pages.size) {
|
||||
return@launchJob // TODO
|
||||
}
|
||||
pages.getOrNull(position)?.let { page ->
|
||||
val centerPos = (lowerPos + upperPos) / 2
|
||||
pages.getOrNull(centerPos)?.let { page ->
|
||||
currentState.update { cs ->
|
||||
cs?.copy(chapterId = page.chapterId, page = page.index)
|
||||
}
|
||||
@@ -282,14 +283,14 @@ class ReaderViewModel @Inject constructor(
|
||||
return@launchJob
|
||||
}
|
||||
ensureActive()
|
||||
if (position >= pages.lastIndex - BOUNDS_PAGE_OFFSET) {
|
||||
if (upperPos >= pages.lastIndex - BOUNDS_PAGE_OFFSET) {
|
||||
loadPrevNextChapter(pages.last().chapterId, isNext = true)
|
||||
}
|
||||
if (position <= BOUNDS_PAGE_OFFSET) {
|
||||
if (lowerPos <= BOUNDS_PAGE_OFFSET) {
|
||||
loadPrevNextChapter(pages.first().chapterId, isNext = false)
|
||||
}
|
||||
if (pageLoader.isPrefetchApplicable()) {
|
||||
pageLoader.prefetch(pages.trySublist(position + 1, position + PREFETCH_LIMIT))
|
||||
pageLoader.prefetch(pages.trySublist(upperPos + 1, upperPos + PREFETCH_LIMIT))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,7 +179,7 @@ abstract class BasePagerReaderFragment : BaseReaderFragment<FragmentReaderPagerB
|
||||
}
|
||||
|
||||
protected open fun notifyPageChanged(page: Int) {
|
||||
viewModel.onCurrentPageChanged(page)
|
||||
viewModel.onCurrentPageChanged(page, page)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@@ -32,7 +32,8 @@ class ReversedReaderFragment : BasePagerReaderFragment() {
|
||||
}
|
||||
|
||||
override fun notifyPageChanged(page: Int) {
|
||||
viewModel.onCurrentPageChanged(reversed(page))
|
||||
val pos = reversed(page)
|
||||
viewModel.onCurrentPageChanged(pos, pos)
|
||||
}
|
||||
|
||||
private fun reversed(position: Int): Int {
|
||||
|
||||
@@ -24,7 +24,8 @@ import org.koitharu.kotatsu.reader.ui.pager.ReaderPage
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class WebtoonReaderFragment : BaseReaderFragment<FragmentReaderWebtoonBinding>() {
|
||||
class WebtoonReaderFragment : BaseReaderFragment<FragmentReaderWebtoonBinding>(),
|
||||
WebtoonRecyclerView.OnWebtoonScrollListener {
|
||||
|
||||
@Inject
|
||||
lateinit var networkState: NetworkState
|
||||
@@ -46,7 +47,7 @@ class WebtoonReaderFragment : BaseReaderFragment<FragmentReaderWebtoonBinding>()
|
||||
with(binding.recyclerView) {
|
||||
setHasFixedSize(true)
|
||||
adapter = readerAdapter
|
||||
addOnPageScrollListener(PageScrollListener())
|
||||
addOnPageScrollListener(this@WebtoonReaderFragment)
|
||||
recyclerLifecycleDispatcher = RecyclerViewLifecycleDispatcher().also {
|
||||
addOnScrollListener(it)
|
||||
}
|
||||
@@ -70,6 +71,15 @@ class WebtoonReaderFragment : BaseReaderFragment<FragmentReaderWebtoonBinding>()
|
||||
exceptionResolver = exceptionResolver,
|
||||
)
|
||||
|
||||
override fun onScrollChanged(
|
||||
recyclerView: WebtoonRecyclerView,
|
||||
dy: Int,
|
||||
firstVisiblePosition: Int,
|
||||
lastVisiblePosition: Int,
|
||||
) {
|
||||
viewModel.onCurrentPageChanged(firstVisiblePosition, lastVisiblePosition)
|
||||
}
|
||||
|
||||
override suspend fun onPagesChanged(pages: List<ReaderPage>, pendingState: ReaderState?) = coroutineScope {
|
||||
val setItems = launch {
|
||||
requireAdapter().setItems(pages)
|
||||
@@ -91,7 +101,7 @@ class WebtoonReaderFragment : BaseReaderFragment<FragmentReaderWebtoonBinding>()
|
||||
?.restoreScroll(pendingState.scroll)
|
||||
}
|
||||
}
|
||||
notifyPageChanged(position)
|
||||
viewModel.onCurrentPageChanged(position, position)
|
||||
} else {
|
||||
Snackbar.make(requireView(), R.string.not_found_404, Snackbar.LENGTH_SHORT)
|
||||
.show()
|
||||
@@ -121,10 +131,6 @@ class WebtoonReaderFragment : BaseReaderFragment<FragmentReaderWebtoonBinding>()
|
||||
viewBinding?.frame?.onZoomOut()
|
||||
}
|
||||
|
||||
private fun notifyPageChanged(page: Int) {
|
||||
viewModel.onCurrentPageChanged(page)
|
||||
}
|
||||
|
||||
override fun switchPageBy(delta: Int) {
|
||||
with(requireViewBinding().recyclerView) {
|
||||
if (isAnimationEnabled()) {
|
||||
@@ -147,12 +153,4 @@ class WebtoonReaderFragment : BaseReaderFragment<FragmentReaderWebtoonBinding>()
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private inner class PageScrollListener : WebtoonRecyclerView.OnPageScrollListener() {
|
||||
|
||||
override fun onPageChanged(recyclerView: WebtoonRecyclerView, index: Int) {
|
||||
super.onPageChanged(recyclerView, index)
|
||||
notifyPageChanged(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,8 +6,8 @@ import android.view.View
|
||||
import androidx.core.view.ViewCompat.TYPE_TOUCH
|
||||
import androidx.core.view.forEach
|
||||
import androidx.core.view.iterator
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.koitharu.kotatsu.core.util.ext.findCenterViewPosition
|
||||
import java.util.LinkedList
|
||||
import java.util.WeakHashMap
|
||||
|
||||
@@ -15,7 +15,8 @@ class WebtoonRecyclerView @JvmOverloads constructor(
|
||||
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
|
||||
) : RecyclerView(context, attrs, defStyleAttr) {
|
||||
|
||||
private var onPageScrollListeners: MutableList<OnPageScrollListener>? = null
|
||||
private var onPageScrollListeners = LinkedList<OnWebtoonScrollListener>()
|
||||
private val scrollDispatcher = WebtoonScrollDispatcher()
|
||||
private val detachedViews = WeakHashMap<View, Unit>()
|
||||
private var isFixingScroll: Boolean = false
|
||||
|
||||
@@ -103,22 +104,20 @@ class WebtoonRecyclerView @JvmOverloads constructor(
|
||||
return 0
|
||||
}
|
||||
|
||||
fun addOnPageScrollListener(listener: OnPageScrollListener) {
|
||||
val list = onPageScrollListeners ?: LinkedList<OnPageScrollListener>().also { onPageScrollListeners = it }
|
||||
list.add(listener)
|
||||
fun addOnPageScrollListener(listener: OnWebtoonScrollListener) {
|
||||
onPageScrollListeners.add(listener)
|
||||
}
|
||||
|
||||
fun removeOnPageScrollListener(listener: OnPageScrollListener) {
|
||||
onPageScrollListeners?.remove(listener)
|
||||
fun removeOnPageScrollListener(listener: OnWebtoonScrollListener) {
|
||||
onPageScrollListeners.remove(listener)
|
||||
}
|
||||
|
||||
private fun notifyScrollChanged(dy: Int) {
|
||||
val listeners = onPageScrollListeners
|
||||
if (listeners.isNullOrEmpty()) {
|
||||
if (listeners.isEmpty()) {
|
||||
return
|
||||
}
|
||||
val centerPosition = findCenterViewPosition()
|
||||
listeners.forEach { it.dispatchScroll(this, dy, centerPosition) }
|
||||
scrollDispatcher.dispatchScroll(this, dy)
|
||||
}
|
||||
|
||||
fun relayoutChildren() {
|
||||
@@ -162,20 +161,30 @@ class WebtoonRecyclerView @JvmOverloads constructor(
|
||||
else -> false
|
||||
}
|
||||
|
||||
abstract class OnPageScrollListener {
|
||||
private class WebtoonScrollDispatcher {
|
||||
|
||||
private var lastPosition = NO_POSITION
|
||||
private var firstPos = NO_POSITION
|
||||
private var lastPos = NO_POSITION
|
||||
|
||||
fun dispatchScroll(recyclerView: WebtoonRecyclerView, dy: Int, centerPosition: Int) {
|
||||
onScroll(recyclerView, dy)
|
||||
if (centerPosition != NO_POSITION && centerPosition != lastPosition) {
|
||||
lastPosition = centerPosition
|
||||
onPageChanged(recyclerView, centerPosition)
|
||||
fun dispatchScroll(rv: WebtoonRecyclerView, dy: Int) {
|
||||
val lm = rv.layoutManager as? LinearLayoutManager
|
||||
if (lm == null) {
|
||||
firstPos = NO_POSITION
|
||||
lastPos = NO_POSITION
|
||||
return
|
||||
}
|
||||
val newFirstPos = lm.findFirstVisibleItemPosition()
|
||||
val newLastPos = lm.findLastVisibleItemPosition()
|
||||
if (newFirstPos != firstPos || newLastPos != lastPos) {
|
||||
firstPos = newFirstPos
|
||||
lastPos = newLastPos
|
||||
rv.onPageScrollListeners.forEach { it.onScrollChanged(rv, dy, newFirstPos, newLastPos) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
open fun onScroll(recyclerView: WebtoonRecyclerView, dy: Int) = Unit
|
||||
interface OnWebtoonScrollListener {
|
||||
|
||||
open fun onPageChanged(recyclerView: WebtoonRecyclerView, index: Int) = Unit
|
||||
fun onScrollChanged(recyclerView: WebtoonRecyclerView, dy: Int, firstVisiblePosition: Int, lastVisiblePosition: Int)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user