🚀

Jetpack Composeで長押し中インクリメントするボタンを作る

2021/11/21に公開

Jetpack Composeで長押し中インクリメントするボタンを作る

前提

  • version
    • Kotlin 1.5.31
    • Jetpack Compose 1.0.5

概要

自作のアプリを作るにあたり、長押しでインクリメントし続けるカウントボタンが必要となりました。
どのような方法で解決したかをメモしておきます。

方法

長押しの検知

Modifier.pointerInteropFilterを使用することで解決できました。
pointerInteropFileterでは、MotionEventを受け取ることができ、
MotionEventに応じた処理を書くことができます。

今回、Button押下と、Buttonを離した時のイベントを取る必要があったため
ACTION_DOWN(押下イベント)とACTION_UP(離したイベント)を使用し,
それぞれのイベント時に任意の処理をすることとしました。

Modifier.pointerInteropFilter {
                when (it.action) {
                    MotionEvent.ACTION_DOWN -> TODO()
                    MotionEvent.ACTION_UP -> TODO()
                }
                true
            },

押下中のインクリメント

押下中のインクリメントの処理はTimerを使って解決しました。

基本的な流れは、押下時にTimer.scheduleを設定して、離した際にキャンセルするというものになります。

Timerの生成に関しては、ACTION_DOWN時にTimerを生成をするようにしています。
Cancel済のTimerを使用するとエラーが出てしまうため、使用する際に毎回生成する必要があります。

@OptIn(ExperimentalComposeUiApi::class)
@Composable
fun ContinuouslyCountableButton(
    modifier: Modifier,
    onRepeat: () -> Unit,
    repeatTimeMilliSeconds: Long = 200
) {
    val timer = remember {
        mutableStateOf<Timer?>(null)
    }
    Button(
        modifier = modifier
            .pointerInteropFilter {
                when (it.action) {
                    MotionEvent.ACTION_DOWN -> {
			timer.value = Timer()
                        timer.value?.schedule(0, repeatTimeMilliSeconds) {
                            onRepeat()
                        }
                    }
                    MotionEvent.ACTION_UP -> {
                        timer.value?.cancel()
                        timer.value = null
                    }
                }
                true
            },
        onClick = {}) {}
}

まとめ

上記二つの方法を用いることで、長押し中カウントアップするボタンを作ることができました。
しかし、これだけでは1のみカウントアップしたい時に複数回カウントアップされてしまうので、
何かしらの工夫をする必要があるように思いました。

また、pointerInteropFilterはExperimentalなので注意して使う必要がありそうです。

間違いや良い方法などあれば、ご指摘ください。

参考

how-to-detect-motion-events-on-a-compose-button - stackoverflow

Discussion