diff --git a/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandlerOrchestrator.kt b/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandlerOrchestrator.kt index d44513339d..b92c87b9e3 100644 --- a/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandlerOrchestrator.kt +++ b/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandlerOrchestrator.kt @@ -19,13 +19,9 @@ class GestureHandlerOrchestrator( * traversing view hierarchy and looking for gesture handlers. */ var minimumAlphaForTraversal = DEFAULT_MIN_ALPHA_FOR_TRAVERSAL - - private val gestureHandlers = arrayOfNulls?>(SIMULTANEOUS_GESTURE_HANDLER_LIMIT) - private val awaitingHandlers = arrayOfNulls?>(SIMULTANEOUS_GESTURE_HANDLER_LIMIT) - private val preparedHandlers = arrayOfNulls?>(SIMULTANEOUS_GESTURE_HANDLER_LIMIT) - private val handlersToCancel = arrayOfNulls?>(SIMULTANEOUS_GESTURE_HANDLER_LIMIT) - private var gestureHandlersCount = 0 - private var awaitingHandlersCount = 0 + private val gestureHandlers = arrayListOf>() + private val awaitingHandlers = arrayListOf>() + private val preparedHandlers = arrayListOf>() private var isHandlingTouch = false private var handlingChangeSemaphore = 0 private var finishedHandlersCleanupScheduled = false @@ -60,23 +56,9 @@ class GestureHandlerOrchestrator( } } - private inline fun compactHandlersIf(handlers: Array?>, count: Int, predicate: (handler: GestureHandler<*>?) -> Boolean): Int { - var out = 0 - for (i in 0 until count) { - if (predicate(handlers[i])) { - handlers[out++] = handlers[i] - } - } - return out - } - private fun cleanupFinishedHandlers() { - var shouldCleanEmptyCells = false - for (i in gestureHandlersCount - 1 downTo 0) { - val handler = gestureHandlers[i]!! + for (handler in gestureHandlers.asReversed()) { if (isFinished(handler.state) && !handler.isAwaiting) { - gestureHandlers[i] = null - shouldCleanEmptyCells = true handler.reset() handler.apply { isActive = false @@ -85,17 +67,14 @@ class GestureHandlerOrchestrator( } } } - if (shouldCleanEmptyCells) { - gestureHandlersCount = compactHandlersIf(gestureHandlers, gestureHandlersCount) { handler -> - handler != null - } - } + + gestureHandlers.removeAll { isFinished(it.state) && !it.isAwaiting } + finishedHandlersCleanupScheduled = false } private fun hasOtherHandlerToWaitFor(handler: GestureHandler<*>): Boolean { - for (i in 0 until gestureHandlersCount) { - val otherHandler = gestureHandlers[i]!! + for (otherHandler in gestureHandlers) { if (!isFinished(otherHandler.state) && shouldHandlerWaitForOther(handler, otherHandler)) { return true } @@ -115,9 +94,7 @@ class GestureHandlerOrchestrator( } private fun cleanupAwaitingHandlers() { - awaitingHandlersCount = compactHandlersIf(awaitingHandlers, awaitingHandlersCount) { handler -> - handler!!.isAwaiting - } + awaitingHandlers.removeAll { !it.isAwaiting } } /*package*/ @@ -125,9 +102,8 @@ class GestureHandlerOrchestrator( handlingChangeSemaphore += 1 if (isFinished(newState)) { // if there were handlers awaiting completion of this handler, we can trigger active state - for (i in 0 until awaitingHandlersCount) { - val otherHandler = awaitingHandlers[i] - if (shouldHandlerWaitForOther(otherHandler!!, handler)) { + for (otherHandler in awaitingHandlers) { + if (shouldHandlerWaitForOther(otherHandler, handler)) { if (newState == GestureHandler.STATE_END) { // gesture has ended, we need to kill the awaiting handler otherHandler.cancel() @@ -182,21 +158,15 @@ class GestureHandlerOrchestrator( shouldResetProgress = true activationIndex = this@GestureHandlerOrchestrator.activationIndex++ } - var toCancelCount = 0 - // Cancel all handlers that are required to be cancel upon current handler's activation - for (i in 0 until gestureHandlersCount) { - val otherHandler = gestureHandlers[i]!! + + for (otherHandler in gestureHandlers.asReversed()) { if (shouldHandlerBeCancelledBy(otherHandler, handler)) { - handlersToCancel[toCancelCount++] = otherHandler + otherHandler.cancel() } } - for (i in toCancelCount - 1 downTo 0) { - handlersToCancel[i]!!.cancel() - } // Clear all awaiting handlers waiting for the current handler to fail - for (i in awaitingHandlersCount - 1 downTo 0) { - val otherHandler = awaitingHandlers[i]!! + for (otherHandler in awaitingHandlers.reversed()) { if (shouldHandlerBeCancelledBy(otherHandler, handler)) { otherHandler.cancel() otherHandler.isAwaiting = false @@ -218,32 +188,31 @@ class GestureHandlerOrchestrator( private fun deliverEventToGestureHandlers(event: MotionEvent) { // Copy handlers to "prepared handlers" array, because the list of active handlers can change // as a result of state updates - val handlersCount = gestureHandlersCount + preparedHandlers.clear() + preparedHandlers.addAll(gestureHandlers) - gestureHandlers.copyInto(preparedHandlers, 0, 0, handlersCount) // We want to deliver events to active handlers first in order of their activation (handlers // that activated first will first get event delivered). Otherwise we deliver events in the // order in which handlers has been added ("most direct" children goes first). Therefore we rely // on Arrays.sort providing a stable sort (as children are registered in order in which they // should be tested) - preparedHandlers.sortWith(handlersComparator, 0, handlersCount) - for (i in 0 until handlersCount) { - deliverEventToGestureHandler(preparedHandlers[i]!!, event) + preparedHandlers.sortWith(handlersComparator) + for (handler in preparedHandlers) { + deliverEventToGestureHandler(handler, event) } } private fun cancelAll() { - for (i in awaitingHandlersCount - 1 downTo 0) { - awaitingHandlers[i]!!.cancel() + for (handler in awaitingHandlers.reversed()) { + handler.cancel() } // Copy handlers to "prepared handlers" array, because the list of active handlers can change // as a result of state updates - val handlersCount = gestureHandlersCount - for (i in 0 until handlersCount) { - preparedHandlers[i] = gestureHandlers[i] - } - for (i in handlersCount - 1 downTo 0) { - preparedHandlers[i]!!.cancel() + preparedHandlers.clear() + preparedHandlers.addAll(gestureHandlers) + + for (handler in gestureHandlers.reversed()) { + handler.cancel() } } @@ -325,7 +294,7 @@ class GestureHandlerOrchestrator( return parent === wrapperView } - fun isAnyHandlerActive() = gestureHandlers.any { it?.state == GestureHandler.STATE_ACTIVE } + fun isAnyHandlerActive() = gestureHandlers.any { it.state == GestureHandler.STATE_ACTIVE } /** * Transforms an event in the coordinates of wrapperView into the coordinate space of the received view. @@ -399,13 +368,11 @@ class GestureHandlerOrchestrator( } private fun addAwaitingHandler(handler: GestureHandler<*>) { - for (i in 0 until awaitingHandlersCount) { - if (awaitingHandlers[i] === handler) { - return - } + if (awaitingHandlers.contains(handler)) { + return } - check(awaitingHandlersCount < awaitingHandlers.size) { "Too many recognizers" } - awaitingHandlers[awaitingHandlersCount++] = handler + + awaitingHandlers.add(handler) with(handler) { isAwaiting = true activationIndex = this@GestureHandlerOrchestrator.activationIndex++ @@ -413,13 +380,11 @@ class GestureHandlerOrchestrator( } private fun recordHandlerIfNotPresent(handler: GestureHandler<*>, view: View) { - for (i in 0 until gestureHandlersCount) { - if (gestureHandlers[i] === handler) { - return - } + if (gestureHandlers.contains(handler)) { + return } - check(gestureHandlersCount < gestureHandlers.size) { "Too many recognizers" } - gestureHandlers[gestureHandlersCount++] = handler + + gestureHandlers.add(handler) handler.isActive = false handler.isAwaiting = false handler.activationIndex = Int.MAX_VALUE @@ -608,7 +573,6 @@ class GestureHandlerOrchestrator( companion object { // The limit doesn't necessarily need to exists, it was just simpler to implement it that way // it is also more allocation-wise efficient to have a fixed limit - private const val SIMULTANEOUS_GESTURE_HANDLER_LIMIT = 20 // Be default fully transparent views can receive touch private const val DEFAULT_MIN_ALPHA_FOR_TRAVERSAL = 0f