2

I am trying to create an icon button which invokes a lambda when tapped, but if the user presses the button and holds it, then also the lambda should get continuously invoked in fixed intervals.

@Composable fun MyIconButton( someLambda: () -> Unit ) { IconButton(onClick = someLambda) { Icon( // painter and content description ) } } 

Here what I want is that when user presses the button, someLambda should get invoked (which is working fine). Additionally, I also want to invoke someLambda repeatedly (with a gap of 500ms between two invocations) until the user releases the button.

Basically what I want is to detect something like the KeyUp and KeyDown events.

How to achieve this?

8
  • Better to check this once ... you might get some idea stackoverflow.com/questions/65835642/… Commented Oct 16, 2021 at 14:18
  • Thanks for the link, I will check that out. Commented Oct 16, 2021 at 14:29
  • Oh, there you are Perry. Commented Oct 16, 2021 at 15:18
  • @ArpitShukla. I don't get it. At one place you are saying that you just want a button that executes different code blocks based on press and long press, then you suddenly introduce keyboard and ARROW KEYS? A.) Which Android keyboard have you seen that incorporates arrow keys, and second, where did the keyboard enter the picture from at all? You don't want a Composable button? Commented Oct 16, 2021 at 16:13
  • 1
    Actually not, the 'key' word still confuses. Why not just say how to detect motion events on a Button in Compose Commented Oct 16, 2021 at 16:36

3 Answers 3

2

You can use Modifier.pointerInpteropFilter for this:

var job by remember { mutableStateOf<Job?>(null) } val scope = rememberCoroutineScope() Icon( imageVector = Icons.Filled.Favorite, modifier = Modifier .requiredSize(96.dp) .pointerInteropFilter { when (it.action) { MotionEvent.ACTION_DOWN -> { job = scope.launch { while (true) { // trigger event Log.d("foo", "Trigger event") delay(500L) } } } MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> { job?.cancel() job = null } } true }, contentDescription = "Sample icon" ) 

Another solution is to use Modifier.pointerInput:

val scope = rememberCoroutineScope() Icon( imageVector = Icons.Filled.Favorite, modifier = Modifier .requiredSize(96.dp) .pointerInput(Unit) { while (true) { awaitPointerEventScope { awaitFirstDown() val job = scope.launch { while (true) { // trigger event Log.d("foo", "Trigger event") delay(500L) Log.d("foo", "After delay") } } waitForUpOrCancellation() job.cancel() } } }, contentDescription = "Sample icon" ) 
Sign up to request clarification or add additional context in comments.

Comments

1

I'll update the answer by tomorrow, but for now, this should suit your use case

Modifier.pointerInput(Unit) { detectTapGestures( onPress = { /* Called when the gesture starts */ }, onDoubleTap = { /* Called on Double Tap */ }, onLongPress = { /* Called on Long Press */ }, onTap = { /* Called on Tap */ } ) } 

1 Comment

But wait, where's Perry?
1

I solved the problem using this Modifier.pointerInput on my Icon.

Modifier.pointerInput(true) { detectTapGestures(onPress = { coroutineScope { val job = launch { while (true) { // Invoke lambda delay(500) } } tryAwaitRelease() job.cancel() } }) } 

As per the documentation,

onPress is called when the press is detected and PressGestureScope.tryAwaitRelease can be used to detect when pointers have released or the gesture was canceled.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.