简体   繁体   中英

Detect when the user lifts their finger (off the screen)

We have pointerInput for detecting tap, drag and pan events, and it also provides a handy awaitPointerEventScope , the pointer being the finger, for mobile devices here. Now, we do have a awaitFirstDown() for detecting when the finger first makes contact with the screen, but I can't seem to find an upper equivalent to this method.

I have a little widget that I wish to detect taps on, but the thing is that the app is made for such a use-case that the user might be in weird positions during its use, and so I wished to have it trigger the required action on just a touch and lift of the finger. The paranoia is that the user might accidentally 'drag' their finger (even by a millimeter, android still picks it up), and I do not want that to be the case. I could implement a tap as well as a drag listener, but none of them offer a finger-lift detection, as far as I know.

What solution, if there is one as of now, is suitable for the use-case while adhering to and leveraging the declarative nature of Compose while keeping the codebase to a minimum?

pointerInteropFilter is the way to go

Item(

   Modifier.pointerInteropFilter {

      if (it.action == MotionEvent.ACTION_UP) {
        triggerAction()
      }

      true // Consume touch, return false if consumption is not required here

   }

)

Better way, and what is suggested by Android code if you are not using interoperability with existing View code is Modifier.pointerInput()

A special PointerInputModifier that provides access to the underlying MotionEvents originally dispatched to Compose. Prefer pointerInput and use this only for interoperation with existing code that consumes MotionEvents. While the main intent of this Modifier is to allow arbitrary code to access the original MotionEvent dispatched to Compose, for completeness, analogs are provided to allow arbitrary code to interact with the system as if it were an Android View.

val pointerModifier = Modifier
    .pointerInput(Unit) {
        forEachGesture {

            awaitPointerEventScope {
                
                awaitFirstDown()
               // ACTION_DOWN here
               
                do {
                    
                    //This PointerEvent contains details including
                    // event, id, position and more
                    val event: PointerEvent = awaitPointerEvent()
                    // ACTION_MOVE loop

                    // Consuming event prevents other gestures or scroll to intercept
                    event.changes.forEach { pointerInputChange: PointerInputChange ->
                        pointerInputChange.consumePositionChange()
                    }
                } while (event.changes.any { it.pressed })

                // ACTION_UP is here
            }
        }
}

This answer explains in detail how it works, internals and key points to consider when creating your own gestures.

Also this is a gesture library you can check out for onTouchEvent counterpart and 2 for detectTransformGestures with onGestureEnd callback and returns number of pointers down or list of PointerInputChange in onGesture event. Which can be used as

 Modifier.pointerMotionEvents(
    onDown = {
        // When down is consumed
        it.consumeDownChange()
    },
    onMove = {
        // Consuming move prevents scroll other events to not get this move event
        it.consumePositionChange()
    },
    onUp= {}
    delayAfterDownInMillis = 20
)

Edit

As of 1.2.0-beta01 , partial consumes like PointerInputChange.consemePositionChange() , PointerInputChange.consumeDownChange() , and one for consuming all changes PointerInputChange.consumeAllChanges() are deprecated

PointerInputChange.consume()

is the only one to be used preventing other gestures/event.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM