I want to share my solution which I think it could be useful if :
- you are able to add a custom ViewGroup as root layout
- also the view which you want to disappear can be a custom one.
First, we create a custom ViewGroup to intercept touch events:
class OutsideTouchDispatcherLayout @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : FrameLayout(context, attrs, defStyleAttr) {
private val rect = Rect()
override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
if (ev.action == MotionEvent.ACTION_DOWN) {
val x = ev.x.roundToInt()
val y = ev.y.roundToInt()
traverse { view ->
if (view is OutsideTouchInterceptor) {
view.getGlobalVisibleRect(rect)
val isOutside = rect.contains(x, y).not()
if (isOutside) {
view.interceptOutsideTouch(ev)
}
}
}
}
return false
}
interface OutsideTouchInterceptor {
fun interceptOutsideTouch(ev: MotionEvent)
}
}
fun ViewGroup.traverse(process: (View) -> Unit) {
for (i in 0 until childCount) {
val child = getChildAt(i)
process(child)
if (child is ViewGroup) {
child.traverse(process)
}
}
}
As you see, OutsideTouchDispatcherLayout
intercepts touch events and informs each descendent view which implenets OutsideTouchInterceptor
that some touch event occured outside of that view.
Here is how the descendent view could handle this event. Notice that it must implement OutsideTouchInterceptor
interface:
class OutsideTouchInterceptorView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr),
OutsideTouchDispatcherLayout.OutsideTouchInterceptor {
override fun interceptOutsideTouch(ev: MotionEvent) {
visibility = GONE
}
}
Then you have outside touch detection easily just by a child-parent relation:
<?xml version="1.0" encoding="utf-8"?>
<com.example.touchinterceptor.OutsideTouchDispatcherLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.example.touchinterceptor.OutsideTouchInterceptorView
android:layout_width="100dp"
android:layout_height="100dp"
android:background="#eee"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</com.example.touchinterceptor.OutsideTouchDispatcherLayout>