DoubleTap in android [duplicate]
Asked Answered
G

7

32

I need to create a small text area.Within that text area when i double click,it will move to next activity.How could i do this?

Gallipot answered 26/1, 2011 at 12:56 Comment(1)
A longclick or a button would be a more common way to achieve this. A similar question was asked here: #2218170Indonesian
O
63

If you do the setup right, the OnDoubleTapListener, within the GestureListeneris very useful. You dont need to handle each single tap and count time in between. Instead let Android handle for you what a tap, a double-tap, a scroll or fling might be. With the helper class SimpleGestureListener that implements the GestureListener and OnDoubleTapListener you dont need much to do.

findViewById(R.id.touchableText).setOnTouchListener(new OnTouchListener() {
    private GestureDetector gestureDetector = new GestureDetector(Test.this, new GestureDetector.SimpleOnGestureListener() {
        @Override
        public boolean onDoubleTap(MotionEvent e) {
            Log.d("TEST", "onDoubleTap");
            return super.onDoubleTap(e);
        }
        ... // implement here other callback methods like onFling, onScroll as necessary
    });

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        Log.d("TEST", "Raw event: " + event.getAction() + ", (" + event.getRawX() + ", " + event.getRawY() + ")");
        gestureDetector.onTouchEvent(event);
        return true;
    }
});

Note: I tested around quite a while to find out, what the right mixture of return true and return false is. This was the really tricky part here.

Another note: When you test this, do it on a real device, instead of the emulator. I had real trouble getting the mouse fast enough to create an onFling event. Real fingers on real devices seem to be much faster.

Operculum answered 28/10, 2013 at 8:13 Comment(5)
This was really perfect for my case. I needed to prevent user from triggering an animation multiple times. Thx a lot.Neurovascular
Worked great for me! One thing I did have to do is drop the "Test.this" parameter for the GestureDetector. It is depreciated but it works. I kept getting an error: "Error:(35, 79) error: not an enclosing class: Test". Any idea what might have caused that?Cottonweed
I found a fix to be able to pass the context. Add this to the onCreate(): final Context context = getApplicationContext(); Then pass "context" rather than "Test.this".Cottonweed
this works well. But it breaks scrolling in the textview.Alanaalanah
Bonus points for an excellent use of anon-classes.Renaud
P
32

A better alternative is to create a lightweight Abstract Class

public abstract class DoubleClickListener implements OnClickListener {

    private static final long DOUBLE_CLICK_TIME_DELTA = 300;//milliseconds

    long lastClickTime = 0;

    @Override
    public void onClick(View v) {
        long clickTime = System.currentTimeMillis();
        if (clickTime - lastClickTime < DOUBLE_CLICK_TIME_DELTA){
            onDoubleClick(v);
            lastClickTime = 0;
        } else {
            onSingleClick(v);
        }
        lastClickTime = clickTime;
    }

    public abstract void onSingleClick(View v);
    public abstract void onDoubleClick(View v);
}

And use it like

 view.setOnClickListener(new DoubleClickListener() {

        @Override
        public void onSingleClick(View v) {

        }

        @Override
        public void onDoubleClick(View v) {

        }
    });
Prescott answered 11/6, 2015 at 8:33 Comment(3)
will it not call both onsingleclick and ondoubleclick when user tries to double click?Telles
The system double tap timeout is ViewConfiguration.getDoubleTapTimeout() .Psychodynamics
You will always get singleClick. There needs to be a timer of 300msRuskin
V
17

very simple logic use below code

    boolean firstTouch = false;
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            if(event.getAction() == event.ACTION_DOWN){
                if(firstTouch && (Helper.getCurrentTimeInMilliSeconds() - time) <= 300) {
                    //do stuff here for double tap
                    Log.e("** DOUBLE TAP**"," second tap ");
                    firstTouch = false;

                } else {
                    firstTouch = true;
                    time = Helper.getCurrentTimeInMilliSeconds();
                    Log.e("** SINGLE  TAP**"," First Tap time  "+time);
                    return false;
                }
            }
            return true;
    }
Venosity answered 18/7, 2013 at 12:19 Comment(1)
long time= System.currentTimeMillis(); and System.currentTimeMillis() instead of Helper.getCurrentTimeInMilliSeconds()Gastrulation
H
5

----------

I took a different approach for implementing double-tap on android views. I created my own logic for detecting double tap and it's very easy to implement.

Here are the steps for doing this:
1. Set onTouchListener on the view you want to receive the touch event.
2. Implement the onTouch(view,event) method. (In double tap the key is to detect two ACTION_DOWN and ACTION_UP events. For this we will have to calculate the time duration between two successive down-up events).

Here is the logic for achieving this:

    /* variable for counting two successive up-down events */
int clickCount = 0;
/*variable for storing the time of first click*/
long startTime;
/* variable for calculating the total time*/
long duration;
/* constant for defining the time duration between the click that can be considered as double-tap */
static final MAX_DURATION = 500;
@Override
public boolean onTouch (View v, MotionEvent event)
{
    switch(event.getAction() & MotionEvent.ACTION_MASK)
    {
        case MotionEvent.ACTION_DOWN:
            startTime = System.currentTimeMillis();
            clickCount++;
            break;
        case MotionEvent.ACTION_UP:
            long time = System.currentTimeMillis() - startTime;
            duration=  duration + time;
            if(clickCount == 2)
            {
                if(totalTime <= DURATION)
                {
                    Toast.makeText(captureActivity.this, "double tap",Toast.LENGTH_LONG).show();
                }
                clickCount = 0;
                duration = 0;
                break;             
            }
    }
    return true;    
}

==== EDIT ======

For me the above is not acceptable with the changes suggested in the cooment - the time out does not work for the above logic.

use this instead

@Override public boolean onTouch(View paramView, MotionEvent event) { switch(event.getAction() & MotionEvent.ACTION_MASK) {

    case MotionEvent.ACTION_UP:

        clickCount++;

        if (clickCount==1){
            startTime = System.currentTimeMillis();
        }

        else if(clickCount == 2)
        {
            long duration =  System.currentTimeMillis() - startTime;
            if(duration <= ONE_SECOND)
            {                    
                    Toast.makeText(captureActivity.this, "double tap",Toast.LENGTH_LONG).show();
                clickCount = 0;
                duration = 0;
            }else{
                clickCount = 1;
                startTime = System.currentTimeMillis();
            }
            break;             
        }
}
return true;    

}

Hanuman answered 26/1, 2011 at 12:56 Comment(2)
A few small changes were necessary to make the above work. 1) change: static final MAX_DURATION = 500; to static final int MAX_DURATION = 500; 2) change: if(totalTime <= DURATION) to if(duration <= MAX_DURATION)Selfpreservation
birdman your changes helped but the timeout does not work, check my edit for corrected timeout logic.Cursed
S
1
X_View.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onItemClick(View view) 
            {
    long timeNow=Calendar.getInstance().getTimeInMillis();
    long timeLastTapped=Long.valueOf(view.getTag().toString()); // Initially set to zero in adapter
    final int minDurationBetweenDoubleTap=500;
    if(timeLastTapped != 0)
            if( timeNow- timeLastTapped < minDurationBetweenDoubleTap) 
                        {
                        Toast.makeText(getApplicationContext(), "DoubleTapped", 10).show(); 
                        }
    view.setTag(""+timeNow); 
}
Smithereens answered 21/5, 2014 at 5:55 Comment(0)
L
0
import android.app.Activity;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.widget.Toast;
public class SimpleGestureFilter extends SimpleOnGestureListener
{
 public final static int SWIPE_UP    = 1; 
 public final static int SWIPE_DOWN  = 2; 
 public final static int SWIPE_LEFT  = 3; 
 public final static int SWIPE_RIGHT = 4;  
 public final static int MODE_TRANSPARENT = 0; 
 public final static int MODE_SOLID = 1; 
 public final static int MODE_DYNAMIC = 2;  
 private final static int ACTION_FAKE = -13;  
 private int swipe_Min_Distance = 100; 
 private int swipe_Max_Distance = 350; 
 private int swipe_Min_Velocity = 100;  
 private int mode = MODE_DYNAMIC; 
 private boolean running = true; 
 private boolean tapIndicator = false;  
 private Activity context; 
 private GestureDetector detector; 
 private SimpleGestureListener listener;   
 public SimpleGestureFilter(Activity context,SimpleGestureListener sgf) 
 {   
  this.context = context;  
  this.detector = new GestureDetector(context, this);  
  this.listener = sgf;  
 }
 public void onTouchEvent(MotionEvent me) 
 {
  // TODO Auto-generated method stub
  if(!this.running)
   return;
  boolean result=this.detector.onTouchEvent(me);
  if(this.mode==MODE_SOLID)
   me.setAction(MotionEvent.ACTION_CANCEL);
  else if(this.mode==MODE_DYNAMIC)
  {
   if(me.getAction()==ACTION_FAKE)
    me.setAction(MotionEvent.ACTION_UP);
   else if(result)
    me.setAction(MotionEvent.ACTION_CANCEL);
   else if(this.tapIndicator)
   {
    me.setAction(MotionEvent.ACTION_DOWN);
    this.tapIndicator=false;
   }
  } 
 }
 public void setMode(int m)
 {
  this.mode=m;
 }
 public int getMode()
 {
  return this.mode;
 }
 public void setEnabled(boolean status)
 {
  this.running=status;
 }
 public void setSwipeMaxDistance(int distance)
 {
  this.swipe_Max_Distance=distance;
 }
 public void setSwipeMinDistance(int distance)
 {
  this.swipe_Min_Distance=distance;
 }
 public int getSwipeMaxDistance()
 {  
  return this.swipe_Max_Distance; 
 }  
 public int getSwipeMinDistance()
 {  
  return this.swipe_Min_Distance; 
 }  
 public int getSwipeMinVelocity()
 {  
  return this.swipe_Min_Velocity; 
 }

 public boolean onFling(MotionEvent e1,MotionEvent e2,float velocityX,float velocityY)
 {
  final float xDistance=Math.abs(e1.getX()-e2.getX());
  final float yDistance=Math.abs(e1.getY()-e2.getY());
  if(xDistance>this.swipe_Max_Distance || yDistance> this.swipe_Max_Distance)

   return false;
  velocityX = Math.abs(velocityX);
  velocityY = Math.abs(velocityY);
  boolean result=false;
  if(velocityX > this.swipe_Min_Velocity && xDistance > this.swipe_Min_Distance)
  {
   if(e1.getX() > e2.getX()) // right to left Move
    this.listener.onSwipe(SWIPE_LEFT);
   else
    this.listener.onSwipe(SWIPE_RIGHT);
   result=true;
  }
  else if(velocityY > this.swipe_Min_Velocity && yDistance > this.swipe_Min_Distance)
  {
   if(e1.getY() > e2.getY()) // bottom to top Move
    this.listener.onSwipe(SWIPE_UP);
   else
    this.listener.onSwipe(SWIPE_DOWN);
   result=true;
  }
  return result;
 }
 public boolean onSingleTapUp(MotionEvent e) 
 {
  this.tapIndicator=true;
  return false;
 }
 public boolean onDoubleTap(MotionEvent e) 
 {
  this.listener.onDoubleTap();
  return false;
 } 
 public boolean onDoubleTapEvent(MotionEvent e) 
 {    
  return true;
 }
 public boolean onSingleTapConfirmed(MotionEvent e) 
 {
  if(this.mode==MODE_DYNAMIC)
  {
   e.setAction(ACTION_FAKE);
   this.context.dispatchTouchEvent(e);
  }
  return false;
 }
 static interface SimpleGestureListener
 {     
  void onSwipe(int direction);     
  void onDoubleTap();
 }
}
Landsman answered 26/1, 2011 at 13:8 Comment(0)
R
0

I had i similar problem and the solutions work until i wanted to do other touch events like swiping and onLongPress. Those methods were never invoked so I had to implement an OnDoubleTapListener. I did as follows:

public class MainActivity extends Activity implements OnDoubleTapListener

Then just implement three methods

@Override
public boolean onDoubleTapEvent(MotionEvent e) {
    if(e.getAction()==1)
    {
        Toast.makeText(getApplicationContext(), "DOUBLE TAP",Toast.LENGTH_SHORT).show();
        // TODO Auto-generated method stub
        // Implement code here!!!
    }
    return true;
}

@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
    return true;
}
@Override
public boolean onDoubleTap(MotionEvent e) {
    return true;
}

Just implement the onDoubleTapEvent method. I don't know when the other two methods are invoked, but this works for me

Ricebird answered 29/7, 2013 at 10:7 Comment(1)
how to set the listener?Ameliaamelie

© 2022 - 2024 — McMap. All rights reserved.