Search code examples
androidandroid-studiotouch-eventontouchlistener

ACTION_UP is not triggered for a quick/normal click on OnTouchListener.onTouch


I have a button to trigger some action in MainActivity and I want to draw animation as follows: when user holds the button (ACTION_DOWN) it scales down, when the button is released (ACTION_UP or ACTION_CANCEL) it scales up and go back to its normal state.

This works great when user holds the button for some time, but for a normal click, ACTION_UP never gets called even though ACTION_DOWN does.

I searched in the documentation and checked almost every answer on stackoverflow but didn't find anything useful.

Here's what the documentation says: "While the framework tries to deliver consistent streams of motion events to views, it cannot guarantee it. Some events may be dropped or modified by containing views in the application before they are delivered thereby making the stream of events inconsistent. Views should always be prepared to handle ACTION_CANCEL and should tolerate anomalous situations such as receiving a new ACTION_DOWN without first having received an ACTION_UP for the prior gesture."

And here's my code:

button.setOnTouchListener { view, event ->
            when(event?.action){
                //behaves as expected
                MotionEvent.ACTION_DOWN -> {
                    scaleDown(view)
                }
                //the problem here: ACTION_UP gets called only when user holds the button for some time
                //and not for a quick/ normal click.
                MotionEvent.ACTION_UP -> {
                    scaleUp(view)
                    doSomething()
                }
                //behaves as expected
                MotionEvent.ACTION_CANCEL -> {
                    scaleUp(view)
                }
            }
            true
        }

I have two questions, First: why ACTION_UP never gets called for a quick/normal click?

Second: how to differentiate between normal click and when user holds the button for some time if i want to react in two different ways?


Solution

  • Answering my own question:

    For the first question, the problem was with the animation not the OnTouchListener.

    For the second question, to differentiate between a normal click and a long click, i had to calculate how many milliseconds have passed since ACTION_DOWN was triggered when ACTION_UP or ACTION_CANCEL is triggered.

    If the difference between the two events is less than 300 milliseconds it's considered a normal click, else it's a long click.

    button.setOnTouchListener { view, event ->
        val now = System.currentTimeMillis()
        when(event?.action){
            MotionEvent.ACTION_DOWN -> {
                scaleDown(view)
            }
            MotionEvent.ACTION_UP -> {
                 scaleUp(view)
                 if(now - event.downTime < 300){
                     //normal click
                 }else{
                     //long click
                 }
            }
            MotionEvent.ACTION_CANCEL -> 
                 scaleUp(view)
            }
       }
       true
    }