2

Short version: I want a way to start a time-based counter on an onTouchEvent, and test to see if a certain amount of time has passed before responding, as a manual LongTouch detection.

Explanation: I have a custom imageView that slides in/out of screen on two-fingered flings. I want to add drag events to it, but these need to be quicker than a longpress. I can delay the drag event by using a counter that updates once per onTouchEvent and only triggers the drag on, say, 10 counts, but the counter only updates on touch events and the finger must be moving.

How can I create a time-based counter, an Activity level field that's incremented 60 times a second or somesuch?

Tickled Pink
  • 969
  • 2
  • 14
  • 26

4 Answers4

8

I'm not sure from you question, but it seems that you're trying to implement a catch long click event in your onTouchListener except you need to perform some kind of logic before the ACTION_UP event happens? If so, that's the same exact problem I was having. I also tried using System.nanoTime() but I found a less tricky method. You CAN use a timer, you just have to schedule it on the first ACTION_DOWN event and cancel it when anything adverse happens (like an ACTION_UP which means it's wasn't a long press but just a click, or an ACTION_MOVE with a displacement over a certain threshold). Something like the following:

layout.seyOnTouchListener(new OnTouchListener(){
    private Timer longpressTimer; //won't depend on a motion event to fire
    private final int longpressTimeDownBegin = 500; //0.5 s
    private Point previousPoint;

    switch(event.getAction()){

    case MotionEvent.ACTION_DOWN:{
        longPressTimer = new Timer();
        longpressTimer.schedule(new TimerTask(){
            //whatever happens on a longpress
        }, longpressTimeDownBegin);
        return true; //the parent was also handling long clicks
    }
    case MotionEvent.ACTION_MOVE:{
        Point currentPoint = new Point((int)event.getX(), (int)event.getY());

        if(previousPoint == null){
            previousPoint = currentPoint;
        }
        int dx = Math.abs(currentPoint.x - previousPoint.x);
        int dy = Math.abs(currentPoint.y - previousPoint.y);
        int s = (int) Math.sqrt(dx*dx + dy*dy);
        boolean isActuallyMoving = s >= minDisToMove; //we're moving

        if(isActuallyMoving){ //only restart timer over if we're actually moving (threshold needed because everyone's finger shakes a little)
            cancelLongPress();
            return false; //didn't trigger long press (will be treated as scroll)
        }
        else{ //finger shaking a little, so continue to wait for possible long press
            return true; //still waiting for potential long press
        }
    }
    default:{
        cancelLongPress();
        return false;
    }
    }
}
mpellegr
  • 3,072
  • 3
  • 22
  • 36
  • Probably not much of a difference, but as an optimization I'd recommend removing the Math.sqrt() function as calculating the square root of a number tends to be slow. Just change minDisToMove to minSquareDisToMove, and assign as a threshold the square of the distance. Also see this [answer](http://stackoverflow.com/a/11679788/996298) as an alternative to detect a longpress. – manu3d Nov 05 '12 at 23:15
1

Just look in the source code of android.

Long press in GestureDetector starts a delayed message on "key down". When this message is coming before "key up" it is an long press.

Just a link to the source http://www.devdaily.com/java/jwarehouse/android/core/java/android/view/GestureDetector.java.shtml

stefan bachert
  • 9,413
  • 4
  • 33
  • 40
  • Thanks, but I'm not using gestures. Manually evaluating touch events is more flexible and convenient for what I'm wanting to do. The touch event system provides excellent information except time. Hence the need for a simple clock to measure presses within the touch event. ;) – Tickled Pink Apr 06 '12 at 18:36
  • Sure, nethertheless a look into the source of GestureDetector will show how to solve this. The delayed message is your "clock" – stefan bachert Apr 06 '12 at 18:39
  • Thanks for the source. Looking now. – Tickled Pink Apr 06 '12 at 18:42
1

The way I would approach this would be to set some boolean to true when Action_Down occurs. If action_up occurs then set the boolean to false. Also start a postDelayed set to whatever delay you want when action_down occurs. In the postdelayed, if the boolean that you earlier set to true is still true then do what you want. Sorry for such a wordy answer but that is how I would do it.

testingtester
  • 528
  • 4
  • 11
  • Okay, I see what you're saying. Delay execution and interupt it. That won't wokr though, I think, because I don't execute an ActionUp. I have two actions. 1) Drag icons from the menu 2) Two-fingered fling the menu on/off screen. The delay in the drag is only to allow the second finger to hit the screen, otherwise the drag is launched before the two-fingered fling can be. So basically, if there isn't a second pointer 100ms after ACTION_DOWN, then do a drag – Tickled Pink Apr 06 '12 at 18:41
  • Ahh okay, I see what your saying. If I remember correctly, the first finger down has an Id of 0. I would do the postDelayed thing I stated above but the deciding factor would be whether you recognize a second finger or not or if the second pointerIndex event.getAction = ActionDown or ActionMove. Something like that. Just some ideas – testingtester Apr 06 '12 at 18:49
  • I've found I can just use the SystemClock and measure the time difference from when the first touch happened. :D – Tickled Pink Apr 06 '12 at 19:24
0

Answer: Use system clock (elapsedRealtime()) and measure milliseconds since the press was initiated. Easy once you start to get the hang of it.

Tickled Pink
  • 969
  • 2
  • 14
  • 26