|
|
@ -32,6 +32,13 @@ open class WebtoonRecyclerView @JvmOverloads constructor(
|
|
|
|
private var firstVisibleItemPosition = 0
|
|
|
|
private var firstVisibleItemPosition = 0
|
|
|
|
private var lastVisibleItemPosition = 0
|
|
|
|
private var lastVisibleItemPosition = 0
|
|
|
|
private var currentScale = DEFAULT_RATE
|
|
|
|
private var currentScale = DEFAULT_RATE
|
|
|
|
|
|
|
|
var canZoom = false
|
|
|
|
|
|
|
|
set(value) {
|
|
|
|
|
|
|
|
field = value
|
|
|
|
|
|
|
|
if (!value) {
|
|
|
|
|
|
|
|
zoom(currentScale, DEFAULT_RATE, x, 0f, y, 0f, true)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private val listener = GestureListener()
|
|
|
|
private val listener = GestureListener()
|
|
|
|
private val detector = Detector()
|
|
|
|
private val detector = Detector()
|
|
|
@ -93,8 +100,10 @@ open class WebtoonRecyclerView @JvmOverloads constructor(
|
|
|
|
fromX: Float,
|
|
|
|
fromX: Float,
|
|
|
|
toX: Float,
|
|
|
|
toX: Float,
|
|
|
|
fromY: Float,
|
|
|
|
fromY: Float,
|
|
|
|
toY: Float
|
|
|
|
toY: Float,
|
|
|
|
|
|
|
|
force: Boolean = false
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
|
|
|
|
if (!canZoom && !force) return
|
|
|
|
isZooming = true
|
|
|
|
isZooming = true
|
|
|
|
val animatorSet = AnimatorSet()
|
|
|
|
val animatorSet = AnimatorSet()
|
|
|
|
val translationXAnimator = ValueAnimator.ofFloat(fromX, toX)
|
|
|
|
val translationXAnimator = ValueAnimator.ofFloat(fromX, toX)
|
|
|
@ -129,6 +138,7 @@ open class WebtoonRecyclerView @JvmOverloads constructor(
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fun zoomFling(velocityX: Int, velocityY: Int): Boolean {
|
|
|
|
fun zoomFling(velocityX: Int, velocityY: Int): Boolean {
|
|
|
|
|
|
|
|
if (!canZoom) return false
|
|
|
|
if (currentScale <= 1f) return false
|
|
|
|
if (currentScale <= 1f) return false
|
|
|
|
|
|
|
|
|
|
|
|
val distanceTimeFactor = 0.4f
|
|
|
|
val distanceTimeFactor = 0.4f
|
|
|
@ -157,6 +167,7 @@ open class WebtoonRecyclerView @JvmOverloads constructor(
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private fun zoomScrollBy(dx: Int, dy: Int) {
|
|
|
|
private fun zoomScrollBy(dx: Int, dy: Int) {
|
|
|
|
|
|
|
|
if (!canZoom) return
|
|
|
|
if (dx != 0) {
|
|
|
|
if (dx != 0) {
|
|
|
|
x = getPositionX(x + dx)
|
|
|
|
x = getPositionX(x + dx)
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -171,6 +182,7 @@ open class WebtoonRecyclerView @JvmOverloads constructor(
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fun onScale(scaleFactor: Float) {
|
|
|
|
fun onScale(scaleFactor: Float) {
|
|
|
|
|
|
|
|
if (!canZoom) return
|
|
|
|
currentScale *= scaleFactor
|
|
|
|
currentScale *= scaleFactor
|
|
|
|
currentScale = currentScale.coerceIn(
|
|
|
|
currentScale = currentScale.coerceIn(
|
|
|
|
MIN_RATE,
|
|
|
|
MIN_RATE,
|
|
|
@ -193,13 +205,13 @@ open class WebtoonRecyclerView @JvmOverloads constructor(
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fun onScaleBegin() {
|
|
|
|
fun onScaleBegin() {
|
|
|
|
if (detector.isDoubleTapping) {
|
|
|
|
if (detector.isDoubleTapping && canZoom) {
|
|
|
|
detector.isQuickScaling = true
|
|
|
|
detector.isQuickScaling = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fun onScaleEnd() {
|
|
|
|
fun onScaleEnd() {
|
|
|
|
if (scaleX < MIN_RATE) {
|
|
|
|
if (scaleX < MIN_RATE && canZoom) {
|
|
|
|
zoom(currentScale, MIN_RATE, x, 0f, y, 0f)
|
|
|
|
zoom(currentScale, MIN_RATE, x, 0f, y, 0f)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -217,7 +229,7 @@ open class WebtoonRecyclerView @JvmOverloads constructor(
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fun onDoubleTapConfirmed(ev: MotionEvent) {
|
|
|
|
fun onDoubleTapConfirmed(ev: MotionEvent) {
|
|
|
|
if (!isZooming) {
|
|
|
|
if (!isZooming && canZoom) {
|
|
|
|
if (scaleX != DEFAULT_RATE) {
|
|
|
|
if (scaleX != DEFAULT_RATE) {
|
|
|
|
zoom(currentScale, DEFAULT_RATE, x, 0f, y, 0f)
|
|
|
|
zoom(currentScale, DEFAULT_RATE, x, 0f, y, 0f)
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
@ -250,7 +262,7 @@ open class WebtoonRecyclerView @JvmOverloads constructor(
|
|
|
|
override fun onTouchEvent(ev: MotionEvent): Boolean {
|
|
|
|
override fun onTouchEvent(ev: MotionEvent): Boolean {
|
|
|
|
val action = ev.actionMasked
|
|
|
|
val action = ev.actionMasked
|
|
|
|
val actionIndex = ev.actionIndex
|
|
|
|
val actionIndex = ev.actionIndex
|
|
|
|
|
|
|
|
if (!canZoom) return super.onTouchEvent(ev)
|
|
|
|
when (action) {
|
|
|
|
when (action) {
|
|
|
|
MotionEvent.ACTION_DOWN -> {
|
|
|
|
MotionEvent.ACTION_DOWN -> {
|
|
|
|
scrollPointerId = ev.getPointerId(0)
|
|
|
|
scrollPointerId = ev.getPointerId(0)
|
|
|
@ -297,12 +309,12 @@ open class WebtoonRecyclerView @JvmOverloads constructor(
|
|
|
|
startScroll = true
|
|
|
|
startScroll = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (startScroll) {
|
|
|
|
if (startScroll && canZoom) {
|
|
|
|
isZoomDragging = true
|
|
|
|
isZoomDragging = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (isZoomDragging) {
|
|
|
|
if (isZoomDragging && canZoom) {
|
|
|
|
zoomScrollBy(dx, dy)
|
|
|
|
zoomScrollBy(dx, dy)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|