Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,69 +1,95 @@
package com.swmansion.gesturehandler

import android.view.MotionEvent
import android.view.ScaleGestureDetector
import android.view.ScaleGestureDetector.OnScaleGestureListener
import android.view.ViewConfiguration
import kotlin.math.abs
import kotlin.math.min
import kotlin.math.max
import kotlin.math.sqrt

class PinchGestureHandler : GestureHandler<PinchGestureHandler>() {
var scale = 0.0
private set
var velocity = 0.0
private set
val focalPointX: Float
get() = scaleGestureDetector?.focusX ?: Float.NaN
val focalPointY: Float
get() = scaleGestureDetector?.focusY ?: Float.NaN
var focalPointX: Float = 0f
private set
var focalPointY: Float = 0f
private set

private var scaleGestureDetector: ScaleGestureDetector? = null
private var startingSpan = 0f
private var spanSlop = 0f
private val gestureListener: OnScaleGestureListener = object : OnScaleGestureListener {
override fun onScale(detector: ScaleGestureDetector): Boolean {
val prevScaleFactor: Double = scale
scale *= detector.scaleFactor.toDouble()
val delta = detector.timeDelta
if (delta > 0) {
velocity = (scale - prevScaleFactor) / delta
}
if (abs(startingSpan - detector.currentSpan) >= spanSlop
&& state == STATE_BEGAN) {
activate()
}
return true
}
private var time: Long = 0L

init {
setShouldCancelWhenOutside(false)
// Get average position X within the view.
private fun getX(event: MotionEvent): Float {
var total: Float = 0f
var pointers: Int = event.pointerCount - 1
for (i in 0..pointers) {
total += event.getX(event.getPointerId(i))
}

override fun onScaleBegin(detector: ScaleGestureDetector): Boolean {
startingSpan = detector.currentSpan
return true
return total / (pointers + 1)
}
// Get average position Y within the view.
private fun getY(event: MotionEvent): Float {
var total: Float = 0f
var pointers: Int = event.pointerCount - 1
for (i in 0..pointers) {
total += event.getY(event.getPointerId(i))
}
return total / (pointers + 1)
}

override fun onScaleEnd(detector: ScaleGestureDetector) {
// ScaleGestureDetector thinks that when fingers are 27mm away that's a sufficiently good
// reason to trigger this method giving us no other choice but to ignore it completely.
// Calculate hypothenuse of touches bounding box.
private fun getSpan(event: MotionEvent): Float {
var minX: Float = Float.MAX_VALUE
var minY: Float = Float.MAX_VALUE
var maxX: Float = Float.MIN_VALUE
var maxY: Float = Float.MIN_VALUE
var pointers: Int = event.pointerCount - 1
for (i in 0..pointers) {
var id = event.getPointerId(i)
var x: Float = event.getX(id)
var y: Float = event.getY(id)
minX = min(minX, x)
maxX = max(maxX, x)
minY = min(minY, y)
maxY = max(maxY, y)
}
var spanX: Float = maxX - minX
var spanY: Float = maxY - minY
return sqrt(spanX * spanX + spanY * spanY)
}

override fun onHandle(event: MotionEvent) {
if (state == STATE_UNDETERMINED) {
val context = view!!.context
velocity = 0.0
scale = 1.0
scaleGestureDetector = ScaleGestureDetector(context, gestureListener)
val configuration = ViewConfiguration.get(context)
spanSlop = configuration.scaledTouchSlop.toFloat()
begin()
}
scaleGestureDetector?.onTouchEvent(event)
var activePointers = event.pointerCount
if (event.actionMasked == MotionEvent.ACTION_POINTER_UP) {
activePointers -= 1
}

if (activePointers >= 2) {
focalPointX = getX(event)
focalPointY = getY(event)
var prevTime: Long = time
time = event.getEventTime()

if (state == STATE_UNDETERMINED) {
velocity = 0.0
scale = 1.0
startingSpan = getSpan(event)
begin();
} else {
var lastSpan: Float = getSpan(event)
var prevScale: Double = scale
scale = (lastSpan / startingSpan).toString().toDouble()
var delta: Long = time - prevTime
if (delta > 0) {
velocity = (scale - prevScale) / delta
}

if (state == STATE_BEGAN) {
activate();
}
}
}

if (state == STATE_ACTIVE && activePointers < 2) {
end()
} else if (event.actionMasked == MotionEvent.ACTION_UP) {
Expand All @@ -72,7 +98,6 @@ class PinchGestureHandler : GestureHandler<PinchGestureHandler>() {
}

override fun onReset() {
scaleGestureDetector = null
velocity = 0.0
scale = 1.0
}
Expand Down