Last active
March 2, 2022 19:16
-
-
Save discord-gists/f3a57b8385e9a245b890c9f5478a4316 to your computer and use it in GitHub Desktop.
a simplified code example for how Discord's Android app supports custom panel gesture detection
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class OverlappingPanelsLayout : FrameLayout { | |
private var scrollingSlopPx: Float = 0f | |
private var velocityTracker: VelocityTracker? = null | |
private var isScrollingHorizontally = false | |
private var xFromInterceptActionDown: Float = 0f | |
private var yFromInterceptActionDown: Float = 0f | |
... // initialize scrollingSlopPx and VelocityTracker | |
override fun onInterceptTouchEvent(event: MotionEvent): Boolean { | |
return when (event.actionMasked) { | |
MotionEvent.ACTION_DOWN -> { | |
isScrollingHorizontally = false | |
xFromInterceptActionDown = event.x | |
yFromInterceptActionDown = event.y | |
if (velocityTracker == null) { | |
velocityTracker = VelocityTracker.obtain() | |
velocityTracker?.addMovement(event) | |
} else { | |
velocityTracker?.clear() | |
} | |
false | |
} | |
MotionEvent.ACTION_MOVE -> { | |
if (isScrollingHorizontally) { | |
true | |
} else { | |
// If the horizontal distance in the MotionEvent is more | |
// than the scroll slop, and if the horizontal distance | |
// is greater than the vertical distance, start the | |
// horizontal scroll for the panels. | |
val xDiff = calculateDistanceX( | |
startX = xFromInterceptActionDown, | |
event = event | |
) | |
val yDiff = calculateDistanceY( | |
startY = yFromInterceptActionDown, | |
event = event | |
) | |
if (abs(xDiff) > scrollingSlopPx && | |
abs(xDiff) > abs(yDiff) | |
) { | |
isScrollingHorizontally = true | |
true | |
} else { | |
false | |
} | |
} | |
} | |
MotionEvent.ACTION_CANCEL, MotionEvent.ACTION_UP -> { | |
velocityTracker?.recycle() | |
velocityTracker = null | |
isScrollingHorizontally | |
} | |
else -> false | |
} | |
} | |
override fun onTouchEvent(event: MotionEvent): Boolean { | |
return when (event.actionMasked) { | |
MotionEvent.ACTION_DOWN -> true | |
MotionEvent.ACTION_MOVE -> { | |
velocityTracker?.addMovement(event) | |
translateCenterPanel(event) | |
true | |
} | |
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> { | |
velocityTracker?.addMovement(event) | |
snapOpenOrClose(event) | |
isScrollingHorizontally = false | |
true | |
} | |
else -> false | |
} | |
} | |
private fun translateCenterPanel(event: MotionEvent) { | |
// This line maps the x position from the MotionEvent to the | |
// targeted x position for the center panel. | |
val targetedX = event.rawX + centerPanelDiffX | |
// This line makes sure the center panel stays within bounds | |
// such as the min and max x positions. | |
val normalizedX = getNormalizedX(targetedX) | |
updateCenterPanelX(normalizedX) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment