PointerEvent


Describes a pointer input change event that has occurred at a particular point in time.

Summary

Public constructors

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
PointerKeyboardModifiers

The state of modifier keys during this event.

Cmn
android
MouseEvent?

Original raw native event from AWT.

android
PointerEventType

The primary reason the PointerEvent was sent.

Cmn
android

Extension functions

Offset

Returns the centroid of all pointers that are down and were previously down.

Cmn
Float

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 PointerInputChange.previousPosition and PointerInputChange.position states.

Cmn
Float

Uses the change of the centroid size between the PointerInputChange.previousPosition and PointerInputChange.position to determine how much zoom was intended.

Cmn

Public constructors

PointerEvent

PointerEvent(changes: List<PointerInputChange>)
Parameters
changes: List<PointerInputChange>

The changes.

Public functions

component1

fun component1(): List<PointerInputChange>

copy

fun copy(changes: List<PointerInputChange>, motionEvent: MotionEvent?): PointerEvent

Public properties

buttons

val buttonsPointerButtons

The state of buttons (e.g. mouse or stylus buttons) during this event.

changes

val changesList<PointerInputChange>

The changes.

keyboardModifiers

val keyboardModifiersPointerKeyboardModifiers

The state of modifier keys during this event.

mouseEvent

val mouseEventMouseEvent?

Original raw native event from AWT.

Note, that its type can be different from type, which is sent by Compose. For example, Compose can send synthetic Move event on relayout, but mouseEvent will tell that it is Up event

type

val typePointerEventType

The primary reason the PointerEvent was sent.

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.draw.drawBehind
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.draw.drawBehind
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()
)
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.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.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()
)
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.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()
)