diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/webtoon/WebtoonLayoutManager.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/webtoon/WebtoonLayoutManager.kt new file mode 100644 index 000000000..29f21d08e --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/webtoon/WebtoonLayoutManager.kt @@ -0,0 +1,41 @@ +package org.koitharu.kotatsu.reader.ui.pager.webtoon + +import android.content.Context +import android.util.AttributeSet +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import kotlin.math.sign + +class WebtoonLayoutManager : LinearLayoutManager { + + private var scrollDirection: Int = 0 + + constructor(context: Context) : super(context) + constructor( + context: Context, + orientation: Int, + reverseLayout: Boolean, + ) : super(context, orientation, reverseLayout) + + constructor( + context: Context, + attrs: AttributeSet?, + defStyleAttr: Int, + defStyleRes: Int, + ) : super(context, attrs, defStyleAttr, defStyleRes) + + override fun scrollVerticallyBy(dy: Int, recycler: RecyclerView.Recycler?, state: RecyclerView.State): Int { + scrollDirection = dy.sign + return super.scrollVerticallyBy(dy, recycler, state) + } + + override fun calculateExtraLayoutSpace(state: RecyclerView.State, extraLayoutSpace: IntArray) { + if (state.hasTargetScrollPosition()) { + super.calculateExtraLayoutSpace(state, extraLayoutSpace) + return + } + val pageSize = height + extraLayoutSpace[0] = if (scrollDirection < 0) pageSize else 0 + extraLayoutSpace[1] = if (scrollDirection < 0) 0 else pageSize + } +} \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/webtoon/WebtoonReaderFragment.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/webtoon/WebtoonReaderFragment.kt index 5a9721124..d0c25311d 100644 --- a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/webtoon/WebtoonReaderFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/webtoon/WebtoonReaderFragment.kt @@ -12,7 +12,6 @@ import org.koitharu.kotatsu.reader.ui.ReaderState import org.koitharu.kotatsu.reader.ui.pager.BaseReader import org.koitharu.kotatsu.reader.ui.pager.BaseReaderAdapter import org.koitharu.kotatsu.reader.ui.pager.ReaderPage -import org.koitharu.kotatsu.utils.ext.doOnCurrentItemChanged import org.koitharu.kotatsu.utils.ext.findCenterViewPosition import org.koitharu.kotatsu.utils.ext.firstVisibleItemPosition import org.koitharu.kotatsu.utils.ext.viewLifecycleScope @@ -33,7 +32,7 @@ class WebtoonReaderFragment : BaseReader() { with(binding.recyclerView) { setHasFixedSize(true) adapter = webtoonAdapter - doOnCurrentItemChanged(::notifyPageChanged) + addOnPageScrollListener(PageScrollListener()) } } @@ -93,4 +92,12 @@ class WebtoonReaderFragment : BaseReader() { override fun switchPageTo(position: Int, smooth: Boolean) { binding.recyclerView.firstVisibleItemPosition = position } + + private inner class PageScrollListener : WebtoonRecyclerView.OnPageScrollListener() { + + override fun onPageChanged(recyclerView: WebtoonRecyclerView, index: Int) { + super.onPageChanged(recyclerView, index) + notifyPageChanged(index) + } + } } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/webtoon/WebtoonRecyclerView.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/webtoon/WebtoonRecyclerView.kt index dfb0bad8c..aa28971af 100644 --- a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/webtoon/WebtoonRecyclerView.kt +++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/webtoon/WebtoonRecyclerView.kt @@ -2,25 +2,27 @@ package org.koitharu.kotatsu.reader.ui.pager.webtoon import android.content.Context import android.util.AttributeSet -import androidx.core.view.ViewCompat +import androidx.core.view.ViewCompat.TYPE_TOUCH import androidx.recyclerview.widget.RecyclerView +import org.koitharu.kotatsu.utils.ext.findCenterViewPosition +import java.util.* class WebtoonRecyclerView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : RecyclerView(context, attrs, defStyleAttr) { - override fun startNestedScroll(axes: Int) = startNestedScroll(axes, ViewCompat.TYPE_TOUCH) + private var onPageScrollListeners: MutableList? = null - override fun startNestedScroll(axes: Int, type: Int): Boolean { - return true - } + override fun startNestedScroll(axes: Int) = startNestedScroll(axes, TYPE_TOUCH) + + override fun startNestedScroll(axes: Int, type: Int): Boolean = true override fun dispatchNestedPreScroll( dx: Int, dy: Int, consumed: IntArray?, offsetInWindow: IntArray? - ) = dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow, ViewCompat.TYPE_TOUCH) + ) = dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow, TYPE_TOUCH) override fun dispatchNestedPreScroll( dx: Int, @@ -34,6 +36,7 @@ class WebtoonRecyclerView @JvmOverloads constructor( consumed[0] = 0 consumed[1] = consumedY } + notifyScrollChanged(dy) return consumedY != 0 || dy == 0 } @@ -75,4 +78,39 @@ class WebtoonRecyclerView @JvmOverloads constructor( } return 0 } + + fun addOnPageScrollListener(listener: OnPageScrollListener) { + val list = onPageScrollListeners ?: LinkedList().also { onPageScrollListeners = it } + list.add(listener) + } + + fun removeOnPageScrollListener(listener: OnPageScrollListener) { + onPageScrollListeners?.remove(listener) + } + + private fun notifyScrollChanged(dy: Int) { + val listeners = onPageScrollListeners + if (listeners.isNullOrEmpty()) { + return + } + val centerPosition = findCenterViewPosition() + listeners.forEach { it.dispatchScroll(this, dy, centerPosition) } + } + + abstract class OnPageScrollListener { + + private var lastPosition = NO_POSITION + + fun dispatchScroll(recyclerView: WebtoonRecyclerView, dy: Int, centerPosition: Int) { + onScroll(recyclerView, dy) + if (centerPosition != NO_POSITION && centerPosition != lastPosition) { + lastPosition = centerPosition + onPageChanged(recyclerView, centerPosition) + } + } + + open fun onScroll(recyclerView: WebtoonRecyclerView, dy: Int) = Unit + + open fun onPageChanged(recyclerView: WebtoonRecyclerView, index: Int) = Unit + } } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/utils/ext/ViewExt.kt b/app/src/main/java/org/koitharu/kotatsu/utils/ext/ViewExt.kt index 586e40eef..bfd3959a9 100644 --- a/app/src/main/java/org/koitharu/kotatsu/utils/ext/ViewExt.kt +++ b/app/src/main/java/org/koitharu/kotatsu/utils/ext/ViewExt.kt @@ -91,22 +91,6 @@ fun View.resetTransformations() { rotationY = 0f } -inline fun RecyclerView.doOnCurrentItemChanged(crossinline callback: (Int) -> Unit) { - addOnScrollListener(object : RecyclerView.OnScrollListener() { - - private var lastItem = -1 - - override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { - super.onScrolled(recyclerView, dx, dy) - val item = recyclerView.findCenterViewPosition() - if (item != RecyclerView.NO_POSITION && item != lastItem) { - lastItem = item - callback(item) - } - } - }) -} - fun RecyclerView.findCenterViewPosition(): Int { val centerX = width / 2f val centerY = height / 2f diff --git a/app/src/main/res/layout/fragment_reader_webtoon.xml b/app/src/main/res/layout/fragment_reader_webtoon.xml index bf60f0a5a..2a3020690 100644 --- a/app/src/main/res/layout/fragment_reader_webtoon.xml +++ b/app/src/main/res/layout/fragment_reader_webtoon.xml @@ -6,4 +6,4 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" - app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" /> \ No newline at end of file + app:layoutManager="org.koitharu.kotatsu.reader.ui.pager.webtoon.WebtoonLayoutManager" /> \ No newline at end of file