3

I have a button and I want it to execute an action only while it is long pressed.

I am able to execute the action on the long press already, the problem is I do not know how to stop it once the user is no longer pressing down on the button.

1)How would one go about implementing something like this?

I am using Jetpack Compose on an Android App

barryalan2633
  • 610
  • 7
  • 21

3 Answers3

6

The Gestures API is probably what you are after.

You can use something like:

var longPressActive by remember { mutableStateOf(false) }

Modifier.pointerInput(Unit) {
  detectTapGestures(
    onLongPress = { longPressActive = true }
    onPress = {
      awaitRelease()
      longPressActive = false
    }
  )
}

You may also find other APIs regarding drag that match your requirements. May be worth expanding on what you are ultimately trying to accomplish.

pablisco
  • 14,027
  • 4
  • 48
  • 70
  • just what I was looking for, thank you. – barryalan2633 Dec 14 '21 at 20:44
  • awaitRelease() does not seem to work for longPress, which was my original goal, would you happen to know the reason? – barryalan2633 Dec 14 '21 at 20:54
  • @barryalan2633 I've updated the example with how you may be able to do what you are after. Also, there are other APIs that deal with drag and drop that may help your use case. You can use the drag and drop API without dragging a component, btw. – pablisco Dec 14 '21 at 20:59
  • 1
    This should be accepted answer. Here i have note, if you using scrollable containers like LazyColumn, Pager, etc. you can try tryAwaitRelease() instead awaitRelease() – Enes Kayıklık Sep 26 '22 at 09:22
2

This code is not tested yet. But I believe this is what you are looking for:

Button(
  onClick = { /* TODO */ },
  modifier = Modifier.pointerInteropFilter {
    when(it.action) {
      MotionEvent.ACTION_DOWN -> {
        // User has pressed the button
      }
      MotionEvent.ACTION_UP -> {
        // User is no longer pressing the button
      }
      else -> false
    }
    true
  }
) {
  Text(text = "Click Me")
}

Don't forget about @ExperimentalPointerInput annotation.

Nishant Jalan
  • 844
  • 9
  • 20
0

To do this, the Button has an argument interactionSource. It can be used as follows:

val interactionSource = remember { MutableInteractionSource() }
val isPressed by interactionSource.collectIsPressedAsState()

Button(
    onClick = { /*TODO*/ }
    interactionSource = interactionSource,
) {

}

If you need to perform some action until the button is released, you can use isPressed with LaunchedEffect:

if (isPressed) {
    LaunchedEffect(Unit) {
        // execute some action
    }
}

It is launched in a coroutine scope, which will be canceled as soon as isPressed becomes false.

An other option is using it with DisposableEffect:

if (isPressed) {
    DisposableEffect(Unit) {
        // do some action
        onDispose {
            // cancel some action
        }
    }
}
Phil Dukhov
  • 67,741
  • 15
  • 184
  • 220