🖱️

Jetpack Compose での多重タップを防ぐコードを考えた

2024/11/02に公開

タイトルのとおり、連打されて不具合が起きて困ってたので、防ぐコードを書いた。
なんとなく動いているが、きちんとできているかはわからない。

(2024/11/02 14:31 追記 - どうやらうまく動かない場合があるぽい)
(2024/11/02 23:07 追記 - isClickable を MutableState にすることで改善した)

ClickState

タップ可能かどうかを判定する変数がほしかったので State を作った。

data class ClickState(
    val recycleTimeMillis: Long = 1000,
) {
    var isClickable: Boolean by mutableStateOf(true)
}

rememberClickState()

フラグを保持していてほしいのと、自動的にまたタップできるように戻って欲しいので LaunchedEffect と組み合わせた関数を作成。

@Composable
fun rememberClickState(
    recycleTimeMillis: Long = 1000,
): ClickState {
    val state = remember { ClickState(recycleTimeMillis = recycleTimeMillis) }

    LaunchedEffect(state.isClickable) {
        if (state.isClickable) return@LaunchedEffect
        delay(state.recycleTimeMillis)
        state.isClickable = true
    }

    return state
}

safeClick()

ClickState を扱う関数をシンプルな形で書いた。

fun safeClick(
    clickState: ClickState,
    onClick: () -> Unit,
) {
    if (clickState.isClickable) {
        clickState.isClickable = false
        onClick()
    }
}

使うとき

この時点では以下のようにして使う。

val clickState = rememberClickState()

FooView(
  onClickBack = {
    safeClick(clickState) {
        navController.popBackStack()
    }
  }
}

safe()

カッコが煩わしく感じたので少しだけ簡略化して書けるように safe() を作った。
safeClick() だと名前被りでダメだった。
もうちょっとマシな名前をつけたい気持ちはある。

fun safe(
    clickState: ClickState,
    onClick: () -> Unit,
): () -> Unit {
    return {
        safeClick(
            clickState = clickState,
            onClick = onClick,
        )
    }
}

使うとき

ほんとにちょっとだけ簡略化できる。

val clickState = rememberClickState()

FooView(
  onClickBack = safe(clickState) {
    navController.popBackStack()
  }
}

最後に

結構いい感じに書けたんじゃないだろうか ✨
もっと良い方法があったら教えてほしい。

Discussion