PointerEvent
-
Cmn
class PointerEvent
Describes a pointer input change event that has occurred at a particular point in time.
Summary
Public constructors |
|
---|---|
PointerEvent(changes: List<PointerInputChange>) |
Cmn
android
|
Public functions |
||
---|---|---|
List<PointerInputChange> |
android
|
|
PointerEvent |
copy(changes: List<PointerInputChange>, motionEvent: MotionEvent?) |
android
|
Public properties |
||
---|---|---|
PointerButtons |
The state of buttons (e.g. mouse or stylus buttons) during this event. |
Cmn
android
|
List<PointerInputChange> |
The changes. |
Cmn
android
|
Int |
Returns |
android
|
PointerKeyboardModifiers |
The state of modifier keys during this event. |
Cmn
android
|
MotionEvent? |
The underlying Android |
android
|
PointerEventType |
The primary reason the |
Cmn
android
|
Extension functions |
||
---|---|---|
Offset |
PointerEvent.calculateCentroid(useCurrent: Boolean) Returns the centroid of all pointers that are down and were previously down. |
Cmn
|
Float |
PointerEvent.calculateCentroidSize(useCurrent: Boolean) Returns the average distance from the centroid for all pointers that are currently and were previously down. |
Cmn
|
Offset |
Returns the change in the centroid location between the previous and the current pointers that are down. |
Cmn
|
Float |
Returns the rotation, in degrees, of the pointers between the |
Cmn
|
Float |
Uses the change of the centroid size between the |
Cmn
|
Public constructors
PointerEvent
PointerEvent(changes: List<PointerInputChange>)
Parameters | |
---|---|
changes: List<PointerInputChange> |
The changes. |
Public functions
Public properties
buttons
val buttons: PointerButtons
The state of buttons (e.g. mouse or stylus buttons) during this event.
keyboardModifiers
val keyboardModifiers: PointerKeyboardModifiers
The state of modifier keys during this event.
motionEvent
val motionEvent: MotionEvent?
The underlying Android MotionEvent
that triggered this PointerEvent
.
This property provides access to the raw MotionEvent
for retrieving platform-specific information not yet exposed by the Compose PointerEvent
API (e.g., stylus tilt angle).
Important Considerations:
-
Read-Only: The returned
MotionEvent
is strictly read-only. Modifying it will lead to unpredictable behavior. -
Transient: Do not store a reference to this
MotionEvent
. The Android framework may recycle it, rendering its state undefined and causing errors if accessed later. Access the data only within the scope where thePointerEvent
is received. -
Metadata Only: This
MotionEvent
should not be used for primary input handling logic (e.g., determining pointer position or button presses). Rely on the properties ofPointerEvent
andPointerInputChange
for this purpose. TheMotionEvent
is intended solely for accessing supplemental metadata. -
Nullability: This property will be
null
in two cases: -
The
PointerEvent
was fabricated within Compose (i.e., not directly from a system input event). -
The
PointerEvent
has already been dispatched within the Compose input system. (See androidx.compose.ui.samples.PointerEventMotionEventSample for details).
import androidx.compose.foundation.gestures.awaitEachGesture import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.input.pointer.pointerInput @Composable fun MyComposable() { Box( Modifier.fillMaxSize().pointerInput(Unit) { awaitEachGesture { val pointerEvent = awaitPointerEvent() // Use MotionEvent only for additional metadata. performAction(pointerEvent.motionEvent?.getAxisValue(MotionEvent.AXIS_TILT)) awaitPointerEvent() // Do NOT try to access the MotionEvent of the first pointEvent after awaits // another PointerEvent. The PointerEvent's MotionEvent is set to null when it // finishes dispatch. // In the following line, pointerEvent.motionEvent returns null. performAction(pointerEvent.motionEvent?.getAxisValue(MotionEvent.AXIS_TILT)) } } ) }
Extension functions
calculateCentroid
fun PointerEvent.calculateCentroid(useCurrent: Boolean = true): Offset
Returns the centroid of all pointers that are down and were previously down. If no pointers are down, Offset.Unspecified
is returned. If useCurrent
is true
, the centroid of the PointerInputChange.position
is returned and if false
, the centroid of the PointerInputChange.previousPosition
is returned. Only pointers that are down in both the previous and current state are used to calculate the centroid.
Example Usage:
import androidx.compose.foundation.gestures.awaitEachGesture import androidx.compose.foundation.gestures.awaitFirstDown import androidx.compose.foundation.gestures.calculateCentroid import androidx.compose.foundation.gestures.calculateCentroidSize import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.draw.drawBehind import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Color import androidx.compose.ui.input.pointer.pointerInput var centroidSize by remember { mutableStateOf(0f) } var position by remember { mutableStateOf(Offset.Zero) } Box( Modifier.drawBehind { // Draw a circle where the gesture is drawCircle(Color.Blue, centroidSize, center = position) } .pointerInput(Unit) { awaitEachGesture { awaitFirstDown().also { position = it.position } do { val event = awaitPointerEvent() val size = event.calculateCentroidSize() if (size != 0f) { centroidSize = event.calculateCentroidSize() } val centroid = event.calculateCentroid() if (centroid != Offset.Unspecified) { position = centroid } } while (event.changes.any { it.pressed }) } } .fillMaxSize() )
calculateCentroidSize
fun PointerEvent.calculateCentroidSize(useCurrent: Boolean = true): Float
Returns the average distance from the centroid for all pointers that are currently and were previously down. If no pointers are down, 0
is returned. If useCurrent
is true
, the size of the PointerInputChange.position
is returned and if false
, the size of PointerInputChange.previousPosition
is returned. Only pointers that are down in both the previous and current state are used to calculate the centroid size.
Example Usage:
import androidx.compose.foundation.gestures.awaitEachGesture import androidx.compose.foundation.gestures.awaitFirstDown import androidx.compose.foundation.gestures.calculateCentroid import androidx.compose.foundation.gestures.calculateCentroidSize import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.draw.drawBehind import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Color import androidx.compose.ui.input.pointer.pointerInput var centroidSize by remember { mutableStateOf(0f) } var position by remember { mutableStateOf(Offset.Zero) } Box( Modifier.drawBehind { // Draw a circle where the gesture is drawCircle(Color.Blue, centroidSize, center = position) } .pointerInput(Unit) { awaitEachGesture { awaitFirstDown().also { position = it.position } do { val event = awaitPointerEvent() val size = event.calculateCentroidSize() if (size != 0f) { centroidSize = event.calculateCentroidSize() } val centroid = event.calculateCentroid() if (centroid != Offset.Unspecified) { position = centroid } } while (event.changes.any { it.pressed }) } } .fillMaxSize() )
calculatePan
fun PointerEvent.calculatePan(): Offset
Returns the change in the centroid location between the previous and the current pointers that are down. Pointers that are newly down or raised are not considered in the centroid movement.
Example Usage:
import androidx.compose.foundation.background import androidx.compose.foundation.gestures.awaitEachGesture import androidx.compose.foundation.gestures.awaitFirstDown import androidx.compose.foundation.gestures.calculatePan import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.offset import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.unit.IntOffset val offsetX = remember { mutableStateOf(0f) } val offsetY = remember { mutableStateOf(0f) } Box( Modifier.offset { IntOffset(offsetX.value.roundToInt(), offsetY.value.roundToInt()) } .graphicsLayer() .background(Color.Blue) .pointerInput(Unit) { awaitEachGesture { awaitFirstDown() do { val event = awaitPointerEvent() val offset = event.calculatePan() offsetX.value += offset.x offsetY.value += offset.y } while (event.changes.any { it.pressed }) } } .fillMaxSize() )
calculateRotation
fun PointerEvent.calculateRotation(): Float
Returns the rotation, in degrees, of the pointers between the PointerInputChange.previousPosition
and PointerInputChange.position
states. Only the pointers that are down in both previous and current states are considered.
Example Usage:
import androidx.compose.foundation.background import androidx.compose.foundation.gestures.awaitEachGesture import androidx.compose.foundation.gestures.awaitFirstDown import androidx.compose.foundation.gestures.calculateRotation import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.input.pointer.pointerInput var angle by remember { mutableStateOf(0f) } Box( Modifier.graphicsLayer(rotationZ = angle) .background(Color.Blue) .pointerInput(Unit) { awaitEachGesture { awaitFirstDown() do { val event = awaitPointerEvent() val rotation = event.calculateRotation() angle += rotation } while (event.changes.any { it.pressed }) } } .fillMaxSize() )
calculateZoom
fun PointerEvent.calculateZoom(): Float
Uses the change of the centroid size between the PointerInputChange.previousPosition
and PointerInputChange.position
to determine how much zoom was intended.
Example Usage:
import androidx.compose.foundation.background import androidx.compose.foundation.gestures.awaitEachGesture import androidx.compose.foundation.gestures.awaitFirstDown import androidx.compose.foundation.gestures.calculateZoom import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.input.pointer.pointerInput var zoom by remember { mutableStateOf(1f) } Box( Modifier.graphicsLayer(scaleX = zoom, scaleY = zoom) .background(Color.Blue) .pointerInput(Unit) { awaitEachGesture { awaitFirstDown() do { val event = awaitPointerEvent() zoom *= event.calculateZoom() } while (event.changes.any { it.pressed }) } } .fillMaxSize() )