A stylus can be an exceptionally productive and creative tool. But when users draw, write, or interact with an app using a stylus, they sometimes touch the screen with the palm of their hands. The touch event can be reported to your app before the system recognizes and dismisses the event as an accidental palm touch.
Best practices
Your app must identify extraneous touch events and ignore them. In Jetpack
Compose, you can access the underlying Android MotionEvent from a
PointerEvent. Check the MotionEvent for ACTION_CANCEL, which
indicates that a gesture should stop and may need to be rolled back. On
Android 13 (API level 33) and higher, check for the FLAG_CANCELED flag
on ACTION_CANCEL and ACTION_POINTER_UP events, which provides a
strong signal that the touch was unintentional, such as a palm touch.
Ingredients
Modifier.pointerInput(): The Compose modifier used to process pointer input.PointerEvent: Represents a pointer event in Compose, which contains one or more changes.PointerEvent.motionEvent: Extension property that provides access to the underlying AndroidMotionEvent(nullable).MotionEvent: Represents touch and movement events. Contains the information necessary to determine whether an event should be disregarded.ACTION_CANCEL:MotionEventconstant that indicates a gesture has been canceled; the gesture should stop and may need to be rolled back.ACTION_POINTER_UP:MotionEventconstant that indicates a pointer other than the first pointer has gone up (that is, has relinquished contact with the device screen).FLAG_CANCELED:MotionEventconstant that indicates that the pointer going up caused an unintentional touch event. Added toACTION_POINTER_UPandACTION_CANCELevents on Android 13 (API level 33) and higher.
Steps
Examine MotionEvent objects dispatched to your app. Use the MotionEvent APIs
to determine event characteristics:
- Single-pointer events — Check for
ACTION_CANCEL, which indicates the gesture should stop. On Android 13 and higher, also check forFLAG_CANCELEDto confirm the touch was unintentional. - Multi-pointer events — On Android 13 and higher, check for
ACTION_POINTER_UPandFLAG_CANCELEDto identify unintentional touches.
Respond to these events by stopping the gesture and rolling back any temporary changes.
1. Acquire motion event objects
Use Modifier.pointerInput to process pointer input on a Composable. Within the
pointer input scope, use awaitPointerEventScope and awaitPointerEvent to
receive events, and retrieve the underlying Android MotionEvent using the
motionEvent property:
import androidx.compose.ui.input.pointer.pointerInput Box( modifier = Modifier .fillMaxSize() .pointerInput(Unit) { awaitPointerEventScope { while (true) { val event = awaitPointerEvent() val motionEvent = event.motionEvent if (motionEvent != null) { // Process motion event. } } } } )
2. Determine the event action and flags
Check the MotionEvent for ACTION_CANCEL, which indicates a single-pointer
event on all API levels. On Android 13 and higher, check ACTION_POINTER_UP
for FLAG_CANCELED:
import androidx.compose.ui.input.pointer.pointerInput Box( modifier = Modifier .fillMaxSize() .pointerInput(Unit) { awaitPointerEventScope { while (true) { val event = awaitPointerEvent() val motionEvent = event.motionEvent ?: continue when (motionEvent.actionMasked) { MotionEvent.ACTION_CANCEL -> { // Process canceled single-pointer motion event for all SDK versions. } MotionEvent.ACTION_POINTER_UP -> { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && (motionEvent.flags and MotionEvent.FLAG_CANCELED) == MotionEvent.FLAG_CANCELED ) { // Process canceled multi-pointer motion event for Android 13 and higher. } } } } } } )
3. Undo the gesture
Once you've identified a palm touch, you can undo the onscreen effects of the gesture.
Your app must keep a history of user actions so that unintended inputs such as palm touches can be undone. See Implement a basic drawing app in the Enhance stylus support in an Android app codelab for an example.
Results
Your app can now identify and reject palm touches for multi-pointer events on Android 13 and higher API levels and for single-pointer events on all API levels.
Additional resources
For more information, see the following:
- Android 13 features and APIs — Improved palm rejection
- Developer guides
- Advanced stylus features
- Palm rejection section of Input compatibility on large screens
- Codelab — Enhance stylus support in an Android app