Search code examples
kotlinandroid-jetpack-composeandroid-compose-card

JetPack Compose: Adding click duration


I have a composable component like so:

Card(
    modifier = Modifier
        .fillMaxWidth()
        .then(modifier ?: Modifier),
    backgroundColor = colorResource(id = R.color.Red),
    shape = RoundedCornerShape(percent = 50),
) {
    Row (
        modifier = Modifier
            .size(160.dp)
    ) {
    }
}

When the user clicks and holds the Card I want to check if the user has held the Card for a second. If they held it longer than a second then I want to log "CLICKED" but if they let go before a second then don't log "CLICKED"

How can I achieve this?


Solution

  • This can be done by writing a custom gesture Modifier such as

    fun Modifier.timedClick(
        timeInMillis: Long,
        interactionSource: MutableInteractionSource = remember {MutableInteractionSource()},
        onClick: (Boolean) -> Unit
    ) = composed {
    
        var timeOfTouch = -1L
        LaunchedEffect(key1 = timeInMillis, key2 = interactionSource) {
            interactionSource.interactions
                .onEach { interaction: Interaction ->
                    when (interaction) {
                        is PressInteraction.Press -> {
                            timeOfTouch = System.currentTimeMillis()
                        }
                        is PressInteraction.Release -> {
                            val currentTime = System.currentTimeMillis()
                            onClick(currentTime - timeOfTouch > timeInMillis)
                        }
                        is PressInteraction.Cancel -> {
                            onClick(false)
                        }
                    }
    
                }
                .launchIn(this)
        }
    
        Modifier.clickable(
            interactionSource = interactionSource,
            indication = rememberRipple(),
            onClick = {}
        )
    }
    

    Usage

    val context = LocalContext.current
    
    Card(
        shape = RoundedCornerShape(percent = 50),
    ) {
        Box(
            modifier = Modifier
                .timedClick(
                    timeInMillis = 1000,
                ) { passed: Boolean ->
                    Toast
                        .makeText(
                            context,
                            "Pressed longer than 1000 $passed",
                            Toast.LENGTH_SHORT
                        )
                        .show()
                }
                .fillMaxWidth()
                .height(100.dp),
            contentAlignment = Alignment.Center
        ) {
    
            Text("Hello World")
        }
    }
    

    Result

    enter image description here