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?
7 Answers
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.
- 11,324
- 4
- 36
- 43
-
This was really perfect for my case. I needed to prevent user from triggering an animation multiple times. Thx a lot. – Yoraco Gonzales Feb 19 '15 at 19:22
-
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? – Noah Gary Dec 01 '15 at 07:43
-
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". – Noah Gary Dec 01 '15 at 08:46
-
-
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) {
}
});
- 972
- 2
- 13
- 26
- 33,936
- 20
- 234
- 300
-
4will it not call both onsingleclick and ondoubleclick when user tries to double click? – q126y Nov 14 '17 at 10:06
-
2The system double tap timeout is `ViewConfiguration.getDoubleTapTimeout() `. – Suragch Feb 16 '18 at 01:15
-
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;
}
- 2,872
- 23
- 40
-
1`long time= System.currentTimeMillis();` and `System.currentTimeMillis()` instead of `Helper.getCurrentTimeInMilliSeconds()` – Prabs Jun 28 '17 at 05:49
----------
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;
}
-
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) – birdman Apr 11 '13 at 10:36
-
birdman your changes helped but the timeout does not work, check my edit for corrected timeout logic. – Aiden Fry Apr 12 '13 at 13:07
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);
}
- 1,494
- 12
- 23
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
- 69
- 2
- 12
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();
}
}
- 13,035
- 29
- 108
- 178