android-jetpack-composeandroid-compose-textfield

How to get a callback when a Text element is pressed down and have the callback include information about which character was pressed?


I have a Text element that contains a number of links.

I want to know when the Text element is pressed down and I want to know which character within the Text element was pressed down so that I can change the appearance of the appropriate link.

Is this possible? If so, how?

Addendum

The ClickableText element does offer onClick and onHover callbacks but neither of these trigger when the element is pressed down. The onClick callback is called when the press is released. The onHover callback is called when the press moves from one character in the text to another.


Solution

  • There's a helpful code block at the end of the ClickableText documentation that demonstrates how to configure a Text element to detect "long press" gestures. You can tweak this code block to detect "pressed down" and "press released" events instead of "long press" gestures.

    Here's one way to achieve this:

    var pressedCharacterPosition by remember { mutableStateOf<Int?>(null) }
    
    var layoutResult by remember { mutableStateOf<TextLayoutResult?>(null) }
    
    val pointerInputModifier = Modifier.pointerInput(Unit) {
        detectTapGestures(
            onPress = { offset ->
                val unwrappedLayoutResult = layoutResult ?: return@detectTapGestures
    
                pressedCharacterPosition = unwrappedLayoutResult.getOffsetForPosition(offset)
    
                Log.d("Foo", "onPress: pressedCharacterPosition = $pressedCharacterPosition")
    
                val pressWasReleased = tryAwaitRelease()
    
                Log.d("Foo", "onPress: pressWasReleased = $pressWasReleased")
    
                pressedCharacterPosition = null
            },
            onTap = { offset ->
                val unwrappedLayoutResult = layoutResult ?: return@detectTapGestures
    
                val characterPosition = unwrappedLayoutResult.getOffsetForPosition(offset)
    
                Log.d("Foo", "onTap: characterPosition = $characterPosition")
    
                // perform whatever action you need to here
            }
        )
    }
    
    Text(
        text = getText(pressedCharacterPosition),
        modifier = Modifier.then(pointerInputModifier),
        onTextLayout = { layoutResult = it }
    )
    

    The getText(pressedCharacterPosition: Int?): AnnotatedString function is up to you to define based on your particular needs.

    For a concrete example of all this, see app1 in the jetpack-compose-experiments repository.