Given a nested view structure such as:
How do I disable input on the grey periphery, such that even as the user holds a finger on it, but touches the yellow view, the touch event would register in dispatchTouchEvent()
for that view unhindered?
EDIT: To explain a bit further, I need some kind of palm rejection system in the grey area. In the yellow area the user is able to draw with his finger. All of that works fine, but on some phones with border-less displays you might accidentally touch the grey area, which registers as input and the drawing is ruined.
This happens only when the user is touching the screen in multiple places at once. One might blame the user at this point for buying into the gimmick, but I've tried it myself and it's really easy to accidentally touch the edge, and prevent events from flowing properly.
After I've posted this question, I have come up with a somewhat hacky solution that uses multi-touch events. It works better than before, but because it's not really a multi-touch event, sometimes it goes stale and stops registering inputs altogether. Besides, it would be nice to be able to capture real multi-touch events in the yellow box eventually (for example to make zoom-into-your-drawing gesture).
The basic premise of my solution so far is:
onDraw()
.MotionEvent
in dispatchTouchEvent()
Then:
// ... event:MotionEvent, pointerCoordsOut:MotionEvent.PointerCoords
for (pidx in 0 until event.pointerCount) {
event.getPointerCoords(pidx, pointerCoordsOut)
if (inYellowArea(pointerCoordsOut.x, pointerCoordsOut.y)) {
//pointerCoordsOut now has (x,y) that I need
}
}
Finally, adjust the code to accept ACTION_*
and ACTION_POINTER_*
events, and make them do something reasonable. This was easy enough in the demo case, but I think this is where the solution will ultimately fail.
So I would still love for there to be a proper solution for palm rejection on borders as-if that event was not there at all, as opposed to in my case part of a complicated gesture that I am trying to decipher.
EDIT:
Still open for suggestions.
UPDATED:
layout:
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/darker_gray"
tools:context=".MainActivity">
<FrameLayout
android:id="@+id/orange"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginBottom="64dp"
android:layout_marginEnd="64dp"
android:layout_marginStart="64dp"
android:layout_marginTop="64dp"
android:background="@android:color/holo_orange_dark"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
</FrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
code:
class MainActivity : AppCompatActivity() {
private val touchableRect = Rect()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
root.post {
orange.getGlobalVisibleRect(touchableRect)
}
root.setOnTouchListener { v, event ->
Log.d("logi", "root touched : ${event.actionMasked}")
false
}
orange.setOnTouchListener { v, event ->
Log.d("logi", "orange touched : ${event.actionMasked}")
true
}
}
override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
val isConsumed = ev?.let {
if (touchableRect.contains(it.x.toInt(), it.y.toInt())) {
orange.dispatchTouchEvent(it)
} else {
true
}
}
return isConsumed ?: true
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With