0
0

長押し中はずっとイベントを発火する みたいな処理をやる

Posted at

コード

suspend fun PointerInputScope.detectTapOrLongPressingGestures(
    onTap: () -> Unit,
    onLongPressing: () -> Unit,
    longPressInterval: Long = 100L
) = coroutineScope {
    awaitEachGesture {
        val down = awaitFirstDown()
        val longPressChange = awaitLongPressOrCancellation(down.id)
        if (longPressChange == null) {
            onTap()
            return@awaitEachGesture
        }
        launch {
            do {
                delay(longPressInterval)
                onLongPressing()
            } while (!longPressChange.isConsumed)
        }
        waitForUpOrCancellation()
        longPressChange.consume()
    }
}

連続タップは onTap の連続にしている。
2本指は、両方の指が完全にリリースされればLongPress終了。2本指でもLongPressに満たない間隔で両方の指をリリースすればタップにしている。

使ったもの

PointerInputScope

fun Modifier.pointerInput(key1: Any?, block: suspend PointerInputScope.() -> Unit): Modifier

ポインタイベントが発生中はそのイベントに対する処理を行い、イベントが発生してない時は 次のイベントが発生するまで中断されるスコープ。これをループすることで イベントの待ち受けをやっている。

PointerInputScope.*()

PointerInputScope には gesture を検出するための拡張関数がいくつかある。

PointerInputScope.detectTapGestures(
    onDoubleTap: ((Offset) -> Unit)?,
    onLongPress: ((Offset) -> Unit)?,
    onPress: suspend PressGestureScope.(Offset) -> Unit,
    onTap: ((Offset) -> Unit)?
)

これのonLongPress は 、一度長押しを消費すると 指を離すまで次のポインタイベントは 処理されないので 、今回は使えない。


PointerInputScope.awaitEachGesture(block: suspend AwaitPointerEventScope.() -> Unit)

Gestureを細かく制御するならこっち

AwaitPointerEventScope

そのままだが、ポインタイベントを待ち受けるためのスコープ。
そのための拡張関数が用意されている。

AwaitPointerEventScope.awaitFirstDown(
    requireUnconsumed: Boolean,
    pass: PointerEventPass
)

最初に、指が Downされるまで 待ち受け


AwaitPointerEventScope.awaitLongPressOrCancellation(
    pointerId: PointerId
)

ダブルタップを検出するまで待ち受け


AwaitPointerEventScope.waitForUpOrCancellation(
    pass: PointerEventPass
)

指が Upされるまで待ち受け。2本以上の指によるGestureは全てがUpされるまで待ち受け。

PointerInputChange

await* 関数は PointerInputChange を返す。
これはその時点での指の位置や押されてるかどうか といった Pointerイベントの状態。

val longPressChange = awaitLongPressOrCancellation(down.id)

長押しが検出される前にすべてのポインタが上げられた場合、または他のジェスチャが変更を消費した場合はNULL

キャンセル と ほかGesture による消費 を見分けたい時はどうすればいいんだろう..
今回はnull であればシングルタップとみなしている。

処理をコメントしていく

suspend fun PointerInputScope.detectTapOrLongPressingGestures(
    onTap: () -> Unit,
    onLongPressing: () -> Unit,
    longPressInterval: Long = 100L
) = coroutineScope {
    awaitEachGesture {
        // 最初のDownイベントを待ち受け
        val down = awaitFirstDown()
        // 上のdownイベントの長押しを待ち受け
        val longPressChange = awaitLongPressOrCancellation(down.id)
        // 長押しでなければシングルタップ
        if (longPressChange == null) {
            onTap()
            return@awaitEachGesture
        }
        launch {
            // 長押し が消費される(指が離される)まで 長押しイベントを上げる
            do {
                delay(longPressInterval)
                onLongPressing()
            } while (!longPressChange.isConsumed)
        }
        // 指が離れるのを待ち受け
        waitForUpOrCancellation()
        // 指が離れたら、長押しを消費して 上のループを抜ける
        longPressChange.consume()
    }
}

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0