Improve webtoon zoom fling
This commit is contained in:
@@ -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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user