如何在Jetpack Compose中实现长按连续增减按钮(点击±1,长按每0.5秒±10)
Jetpack Compose实现按钮单击±1、长按连续±10的功能
需求描述
我需要修改现有按钮的交互逻辑:
- 单击按钮时,执行±1的增减操作;
- 长按按钮时,每0.5秒执行一次±10的增减操作,直到用户松开按钮。
当前代码如下:
Box( modifier = Modifier .fillMaxSize() .background(Color.Transparent) .weight(2f) .combinedClickable( onClick = { when (selectedCounter) { "Life" -> if (life > 0) life -= 1 "Poison" -> if (poison > 0) poison -= 1 } consecutiveTapsRemove = maxOf(0, consecutiveTapsRemove + 1) }, onLongClick = { when (selectedCounter) { "Life" -> if (life > 0) life -= 10 "Poison" -> if (poison > 0) poison -= 10 } consecutiveTapsRemove = maxOf(0, consecutiveTapsRemove + 10) } ), ) {} Box( modifier = Modifier .fillMaxSize() .background(Color.Transparent) .weight(2f) .combinedClickable( onClick = { when (selectedCounter) { "Life" -> life += 1 "Poison" -> poison += 1 } consecutiveTapsAdd = maxOf(0, consecutiveTapsAdd + 1) }, onLongClick = { when (selectedCounter) { "Life" -> life += 10 "Poison" -> poison += 10 } consecutiveTapsAdd = maxOf(0, consecutiveTapsAdd + 10) } ) ) {}
实现方案
combinedClickable的onLongClick仅触发一次,无法满足连续执行需求。需要通过pointerInput监听触摸事件,结合协程定时执行增减逻辑,具体实现如下:
1. 封装通用增减逻辑
先封装统一的增减函数,减少代码重复并处理边界值:
// isIncrement:true为加操作,false为减操作 // value:每次增减的数值 // setLife/setPoison:状态更新回调 private fun modifyCounter( isIncrement: Boolean, value: Int, setLife: (Int) -> Unit, setPoison: (Int) -> Unit ) { val delta = if (isIncrement) value else -value when (selectedCounter) { "Life" -> { val newLife = if (isIncrement) life + delta else maxOf(0, life + delta) setLife(newLife) } "Poison" -> { val newPoison = if (isIncrement) poison + delta else maxOf(0, poison + delta) setPoison(newPoison) } } }
2. 重写按钮触摸逻辑
用pointerInput替代combinedClickable,实现完整的触摸交互:
// 减号按钮 Box( modifier = Modifier .fillMaxSize() .background(Color.Transparent) .weight(2f) .pointerInput(Unit) { coroutineScope { while (true) { awaitPointerEventScope { // 等待手指按下 val downEvent = awaitFirstDown() // 执行单击减1逻辑 modifyCounter(false, 1, { life = it }, { poison = it }) consecutiveTapsRemove = maxOf(0, consecutiveTapsRemove + 1) // 等待300ms判定是否长按(系统默认长按阈值,可调整) val isLongPress = withTimeoutOrNull(300) { awaitPointerEvent() } == null if (isLongPress) { launch { // 循环执行减10,直到手指松开 while (downEvent.pressed) { modifyCounter(false, 10, { life = it }, { poison = it }) consecutiveTapsRemove = maxOf(0, consecutiveTapsRemove + 10) delay(500) // 每0.5秒执行一次 // 更新触摸状态,确认是否仍按下 awaitPointerEvent() } } } // 等待手指抬起,结束本次交互 awaitRelease() } } } } ) {} // 加号按钮 Box( modifier = Modifier .fillMaxSize() .background(Color.Transparent) .weight(2f) .pointerInput(Unit) { coroutineScope { while (true) { awaitPointerEventScope { val downEvent = awaitFirstDown() // 执行单击加1逻辑 modifyCounter(true, 1, { life = it }, { poison = it }) consecutiveTapsAdd = maxOf(0, consecutiveTapsAdd + 1) val isLongPress = withTimeoutOrNull(300) { awaitPointerEvent() } == null if (isLongPress) { launch { while (downEvent.pressed) { modifyCounter(true, 10, { life = it }, { poison = it }) consecutiveTapsAdd = maxOf(0, consecutiveTapsAdd + 10) delay(500) awaitPointerEvent() } } } awaitRelease() } } } } ) {}
关键说明
pointerInput是Compose中处理精细触摸事件的核心API,能完整监听按下、移动、抬起全生命周期;- 长按判定时间设为300ms(系统默认值),可根据需求调整;
- 协程循环中通过
delay(500)控制执行间隔,每次循环更新触摸状态确保手指松开时立即停止; - 统一的
modifyCounter函数处理了数值边界(避免life/poison小于0),减少重复代码。
内容的提问来源于stack exchange,提问作者Bjurhager
相关产品推荐
相关产品推荐

