Let's try to design reusable functions to solve this problem and similar ones if the need arises.
We can create extension functions on View
that tell whether a point on the screen is contained within the View
or not.
fun View.containsPoint(rawX: Int, rawY: Int): Boolean {
val rect = Rect()
this.getGlobalVisibleRect(rect)
return rect.contains(rawX, rawY)
}
fun View.doesNotContainPoint(rawX: Int, rawY: Int) = !containsPoint(rawX, rawY)
Now we can override the dispatchTouchEvent(event: MotionEvent)
method of Activity
to know where exactly the user clicked on the screen.
private const val SCROLL_THRESHOLD = 10F // To filter out scroll gestures from clicks
private var downX = 0F
private var downY = 0F
private var isClick = false
override fun dispatchTouchEvent(event: MotionEvent): Boolean {
when (event.action and MotionEvent.ACTION_MASK) {
MotionEvent.ACTION_DOWN -> {
downX = event.x
downY = event.y
isClick = true
}
MotionEvent.ACTION_MOVE -> {
val xThreshCrossed = abs(downX - event.x) > SCROLL_THRESHOLD
val yThreshCrossed = abs(downY - event.y) > SCROLL_THRESHOLD
if (isClick and (xThreshCrossed or yThreshCrossed)) {
isClick = false
}
}
MotionEvent.ACTION_CANCEL, MotionEvent.ACTION_UP -> {
if (isClick) onScreenClick(event.rawX, event.rawY)
}
else -> { }
}
return super.dispatchTouchEvent(event)
}
private fun onScreenClick(rawX: Float, rawY: Float) { }
Now, you can simply use the above-defined functions to achieve the required result
private fun onScreenClick(rawX: Float, rawY: Float) {
if (bottomSheet.doesNotContainPoint(rawX.toInt(), rawY.toInt())) {
// Handle bottomSheet state changes
}
}
What more? If you have a BaseActivity
which is extended by all your Activities
then you can add the click detection code to it. You can make the onScreenClick
an protected open
method so that it can be overridden by the sub-classes.
protected open fun onScreenClick(rawX: Float, rawY: Float) { }
Usage:
override fun onScreenClick(rawX: Float, rawY: Float) {
super.onScreenClick(rawX, rawY)
if (bottomSheet.doesNotContainPoint(rawX.toInt(), rawY.toInt())) {
// Handle bottomSheet state changes
}
}