Use Android quick scale for image zoom
This commit is contained in:
@@ -8,10 +8,7 @@ import android.util.AttributeSet
|
||||
import android.view.GestureDetector
|
||||
import android.view.MotionEvent
|
||||
import android.view.ScaleGestureDetector
|
||||
import android.view.ViewConfiguration
|
||||
import androidx.appcompat.widget.AppCompatImageView
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.exp
|
||||
|
||||
/** A fit-to-screen image which supports pinch zoom and one-finger panning. */
|
||||
class ZoomImageView @JvmOverloads constructor(
|
||||
@@ -29,13 +26,7 @@ class ZoomImageView @JvmOverloads constructor(
|
||||
private var dragging = false
|
||||
private var lastFocusX = 0f
|
||||
private var lastFocusY = 0f
|
||||
private var doubleTapZooming = false
|
||||
private var doubleTapMoved = false
|
||||
private var doubleTapX = 0f
|
||||
private var doubleTapY = 0f
|
||||
private var doubleTapStartY = 0f
|
||||
private var doubleTapBaseScale = 1f
|
||||
private val touchSlop = ViewConfiguration.get(context).scaledTouchSlop
|
||||
private var pointerCount = 0
|
||||
private val scaleDetector = ScaleGestureDetector(context, object : ScaleGestureDetector.SimpleOnScaleGestureListener() {
|
||||
override fun onScaleBegin(detector: ScaleGestureDetector): Boolean {
|
||||
lastFocusX = detector.focusX
|
||||
@@ -44,10 +35,13 @@ class ZoomImageView @JvmOverloads constructor(
|
||||
}
|
||||
|
||||
override fun onScale(detector: ScaleGestureDetector): Boolean {
|
||||
// A ScaleGestureDetector reports focus movement even when the two
|
||||
// fingers stay the same distance apart, which is two-finger panning.
|
||||
// Focus movement while two fingers are down is two-finger panning.
|
||||
// Quick scale has one pointer, so it remains pure zoom around its
|
||||
// built-in double-tap anchor.
|
||||
setScaleAround((currentScale * detector.scaleFactor).coerceIn(minimumScale, minimumScale * MAX_ZOOM), detector.focusX, detector.focusY)
|
||||
transform.postTranslate(detector.focusX - lastFocusX, detector.focusY - lastFocusY)
|
||||
if (pointerCount >= 2) {
|
||||
transform.postTranslate(detector.focusX - lastFocusX, detector.focusY - lastFocusY)
|
||||
}
|
||||
lastFocusX = detector.focusX
|
||||
lastFocusY = detector.focusY
|
||||
constrain()
|
||||
@@ -60,45 +54,11 @@ class ZoomImageView @JvmOverloads constructor(
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onDoubleTap(event: MotionEvent): Boolean = true
|
||||
|
||||
override fun onDoubleTapEvent(event: MotionEvent): Boolean {
|
||||
when (event.actionMasked) {
|
||||
MotionEvent.ACTION_DOWN -> {
|
||||
doubleTapZooming = true
|
||||
doubleTapMoved = false
|
||||
doubleTapX = event.x
|
||||
doubleTapY = event.y
|
||||
doubleTapStartY = event.y
|
||||
doubleTapBaseScale = currentScale
|
||||
}
|
||||
MotionEvent.ACTION_MOVE -> if (doubleTapZooming) {
|
||||
val distance = doubleTapStartY - event.y
|
||||
if (abs(distance) > touchSlop) doubleTapMoved = true
|
||||
val target = (doubleTapBaseScale * exp((distance / DOUBLE_TAP_DRAG_DISTANCE).toDouble()).toFloat())
|
||||
.coerceIn(minimumScale, minimumScale * MAX_ZOOM)
|
||||
setScaleAround(target, doubleTapX, doubleTapY)
|
||||
constrain()
|
||||
}
|
||||
MotionEvent.ACTION_UP -> if (doubleTapZooming) {
|
||||
if (!doubleTapMoved) {
|
||||
val target = if (currentScale > minimumScale * 1.1f) minimumScale else minimumScale * DOUBLE_TAP_ZOOM
|
||||
setScaleAround(target, doubleTapX, doubleTapY)
|
||||
constrain()
|
||||
}
|
||||
doubleTapZooming = false
|
||||
}
|
||||
MotionEvent.ACTION_CANCEL -> doubleTapZooming = false
|
||||
}
|
||||
return true
|
||||
}
|
||||
})
|
||||
|
||||
init {
|
||||
scaleType = ScaleType.MATRIX
|
||||
// We implement double-tap-and-drag ourselves below. Leaving Android's
|
||||
// quick-scale enabled makes both handlers modify the same matrix.
|
||||
scaleDetector.isQuickScaleEnabled = false
|
||||
scaleDetector.isQuickScaleEnabled = true
|
||||
}
|
||||
|
||||
override fun setImageBitmap(bitmap: Bitmap?) {
|
||||
@@ -112,6 +72,7 @@ class ZoomImageView @JvmOverloads constructor(
|
||||
}
|
||||
|
||||
override fun onTouchEvent(event: MotionEvent): Boolean {
|
||||
pointerCount = event.pointerCount
|
||||
scaleDetector.onTouchEvent(event)
|
||||
gestureDetector.onTouchEvent(event)
|
||||
when (event.actionMasked) {
|
||||
@@ -120,7 +81,7 @@ class ZoomImageView @JvmOverloads constructor(
|
||||
lastY = event.y
|
||||
dragging = false
|
||||
}
|
||||
MotionEvent.ACTION_MOVE -> if (event.pointerCount == 1 && !scaleDetector.isInProgress && !doubleTapZooming) {
|
||||
MotionEvent.ACTION_MOVE -> if (event.pointerCount == 1 && !scaleDetector.isInProgress) {
|
||||
val dx = event.x - lastX
|
||||
val dy = event.y - lastY
|
||||
if (dx != 0f || dy != 0f) {
|
||||
@@ -186,7 +147,5 @@ class ZoomImageView @JvmOverloads constructor(
|
||||
|
||||
private companion object {
|
||||
const val MAX_ZOOM = 6f
|
||||
const val DOUBLE_TAP_ZOOM = 2.5f
|
||||
const val DOUBLE_TAP_DRAG_DISTANCE = 280f
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user