From b4bd923ce843532f86d822e4d9baabbab1d66ea3 Mon Sep 17 00:00:00 2001 From: vianh Date: Thu, 15 Feb 2024 21:21:45 +0700 Subject: [PATCH] Improve webtoon zoom fling --- .../ui/pager/webtoon/WebtoonScalingFrame.kt | 37 ++++++++++++------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/pager/webtoon/WebtoonScalingFrame.kt b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/pager/webtoon/WebtoonScalingFrame.kt index 067ae9933..1223480e6 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/pager/webtoon/WebtoonScalingFrame.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/pager/webtoon/WebtoonScalingFrame.kt @@ -3,6 +3,7 @@ package org.koitharu.kotatsu.reader.ui.pager.webtoon import android.animation.ValueAnimator import android.content.Context import android.graphics.Matrix +import android.graphics.Point import android.graphics.Rect import android.graphics.RectF import android.util.AttributeSet @@ -19,12 +20,16 @@ import android.widget.OverScroller import androidx.core.animation.doOnEnd import androidx.core.view.GestureDetectorCompat import androidx.core.view.ViewConfigurationCompat +import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.ui.widgets.ZoomControl import org.koitharu.kotatsu.core.util.ext.getAnimationDuration +import kotlin.math.roundToInt private const val MAX_SCALE = 2.5f private const val MIN_SCALE = 0.5f +private const val FLING_RANGE = 20_000 + class WebtoonScalingFrame @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, @@ -36,6 +41,7 @@ class WebtoonScalingFrame @JvmOverloads constructor( private val scaleDetector = ScaleGestureDetector(context, this) private val gestureDetector = GestureDetectorCompat(context, GestureListener()) private val overScroller = OverScroller(context, AccelerateDecelerateInterpolator()) + private val transformMatrix = Matrix() private val matrixValues = FloatArray(9) private val scale @@ -49,6 +55,7 @@ class WebtoonScalingFrame @JvmOverloads constructor( private val translateBounds = RectF() private val targetHitRect = Rect() private var animator: ValueAnimator? = null + private var pendingScroll = 0 var isZoomEnable = false set(value) { @@ -80,7 +87,7 @@ class WebtoonScalingFrame @JvmOverloads constructor( overScroller.forceFinished(true) } - gestureDetector.onTouchEvent(ev) + val consumed = gestureDetector.onTouchEvent(ev) scaleDetector.onTouchEvent(ev) // Offset event to inside the child view @@ -88,11 +95,7 @@ class WebtoonScalingFrame @JvmOverloads constructor( ev.offsetLocation(halfWidth - ev.x + targetHitRect.width() / 3, 0f) } - // Send action cancel to avoid recycler jump when scale end - if (scaleDetector.isInProgress) { - ev.action = MotionEvent.ACTION_CANCEL - } - return super.dispatchTouchEvent(ev) + return consumed || scaleDetector.isInProgress || super.dispatchTouchEvent(ev) } override fun onGenericMotionEvent(event: MotionEvent): Boolean { @@ -178,6 +181,10 @@ class WebtoonScalingFrame @JvmOverloads constructor( scaleY = scale translationX = transX translationY = transY + if (pendingScroll != 0) { + nestedScrollBy(0, pendingScroll) + pendingScroll = 0 + } } val newHeight = if (scale < 1f) (height / scale).toInt() else height @@ -210,6 +217,7 @@ class WebtoonScalingFrame @JvmOverloads constructor( else -> 0f } + pendingScroll = if (scale > 1) (dy / scale).roundToInt() else 0 transformMatrix.postTranslate(dx, dy) syncMatrixValues() } @@ -277,6 +285,7 @@ class WebtoonScalingFrame @JvmOverloads constructor( private fun findTargetChild() = getChildAt(0) as WebtoonRecyclerView private inner class GestureListener : GestureDetector.SimpleOnGestureListener(), Runnable { + private val prevPos = Point() override fun onScroll( e1: MotionEvent?, @@ -294,7 +303,7 @@ class WebtoonScalingFrame @JvmOverloads constructor( val newScale = if (scale != 1f) 1f else MAX_SCALE * 0.8f ValueAnimator.ofFloat(scale, newScale).run { interpolator = AccelerateDecelerateInterpolator() - duration = 300 + duration = context.getAnimationDuration(R.integer.config_defaultAnimTime) addUpdateListener { scaleChild(it.animatedValue as Float, e.x, e.y) } @@ -311,15 +320,16 @@ class WebtoonScalingFrame @JvmOverloads constructor( ): Boolean { if (scale <= 1) return false + prevPos.set(transX.toInt(), transY.toInt()) overScroller.fling( - transX.toInt(), - transY.toInt(), + prevPos.x, + prevPos.y, velocityX.toInt(), velocityY.toInt(), translateBounds.left.toInt(), translateBounds.right.toInt(), - translateBounds.top.toInt(), - translateBounds.bottom.toInt(), + translateBounds.top.toInt() - FLING_RANGE, + translateBounds.bottom.toInt() + FLING_RANGE, ) postOnAnimation(this) return true @@ -328,9 +338,10 @@ class WebtoonScalingFrame @JvmOverloads constructor( override fun run() { if (overScroller.computeScrollOffset()) { transformMatrix.postTranslate( - overScroller.currX - transX, - overScroller.currY - transY, + overScroller.currX.toFloat() - prevPos.x, + overScroller.currY.toFloat() - prevPos.y ) + prevPos.set(overScroller.currX, overScroller.currY) invalidateTarget() postOnAnimation(this) }