android jetpack compose: ACTION_UP only reached for tap but not after ACTION_MOVE when using pointerInteropFilter

1.3k Views Asked by At

I need to scroll programatically (in order to snap items to adjust symmetrically top and bottom) after scrolling or after a tap (click) in a lazy column. I also need to start at a specific item when app launches - starts.

I am using pointerInteropFilter to be able to run some code at these actions: down, move, up. The code runs ok when I tap but it does not trigger ACTION_UP after a move is done.

[this is the desired result as soon as I release the finger from the screen... that is... a "jump" or scroll to a value that in this cases is item 10 and some offset][1]

The code is working only for tap... but the coroutine is not working when Action_up

I read that we are adviced to "prefer pointerInput] and use this only for interoperation with

  • existing code that consumes [MotionEvent]s"

It also say that pointerinteropFilter try to make a seamless treatment between view and motion events... but I dont know if it is relevant.

Thanks.

Mauricio

@Composable
fun Greeting(name: String) {
    val listState2 = rememberLazyListState()
    val coroutineScope = rememberCoroutineScope()

    LazyColumn(
        state = listState2,
    ) {
        items (50) {index ->
                Text(
                    modifier = Modifier
                        .pointerInteropFilter {

                            when (it.action) {
                                MotionEvent.ACTION_DOWN -> {
                                    Log.i(ContentValues.TAG, "down pressed")
                                    false
                                }
                                MotionEvent.ACTION_MOVE -> {
                                    Log.i(ContentValues.TAG, "moved done")
                                    false
                                }

                                MotionEvent.ACTION_UP -> {
                                    coroutineScope.launch {
                                        listState2.scrollToItem(10, 28)
                                    }
                                    Log.i(ContentValues.TAG, "up detected")
                                }

                                MotionEvent.ACTION_CANCEL -> {
                                    coroutineScope.launch {
                                        listState2.scrollToItem(10, 28)
                                    }
                                    Log.i(ContentValues.TAG, "canceled chosen")
                                    false
                                }

                                else ->
                                    false
                            }
                            true
                        },
                    text = "item $index",
                )
        }
    }
}```




  [1]: https://i.stack.imgur.com/vSiCG.png
1

There are 1 best solutions below

0
Богатов Евгений On

I've faced the same issues.

The onClick= function doesn't work for all descendants, that are placed in the same composable ConstraintLayout, if the layout uses .pointerInteropFilter and returns true as a result of catching ACTION_UP MotionEvent.

It's related to the fact that clickable logic under the hood expects PressInteraction.Press which value is a simple mapping from ACTION_UP. Thus ConstraintLayout by catching ACTION_UP MoutionEvent prevents passing of PressInteraction.Press to descendants.

If the ConstraintLayout will return false on ACTION_UP MoutionEvent, then the next MotionEvents won't be dispatched to it next time. Because of implementation under the hood.

So the root cause of the issue is misusing of the Modifier.pointerInteropFilter. The idea behind the function is filtering events, what it actually does.

In my case looks like it's required to rewrite Touch logic to Modifier.pointerInput.

Maybe this findings will help you as well