In my application, I need to handle both move and click events.
A click is a sequence of one ACTION_DOWN action, several ACTION_MOVE actions and one ACTION_UP action
Use the detector, It works, and it will not raise in case of dragging
Field:
private GestureDetector mTapDetector;
Initialize:
mTapDetector = new GestureDetector(context,new GestureTap());
Inner class:
class GestureTap extends GestureDetector.SimpleOnGestureListener {
@Override
public boolean onDoubleTap(MotionEvent e) {
return true;
}
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
// TODO: handle tap here
return true;
}
}
onTouch:
@Override
public boolean onTouch(View v, MotionEvent event) {
mTapDetector.onTouchEvent(event);
return true;
}
Enjoy :)
It's very difficult for an ACTION_DOWN to occur without an ACTION_MOVE occurring. The slightest twitch of your finger on the screen in a different spot than where the first touch occurred will trigger the MOVE event. Also, I believe a change in finger pressure will also trigger the MOVE event. I would use an if statement in the Action_Move method to try to determine the distance away the move occurred from the original DOWN motion. if the move occurred outside some set radius, your MOVE action would occur. It's probably not the best, resource efficient way to do what your trying but it should work.
Adding to the above answers ,if you want to implement both onClick and Drag actions then my code below can you guys. Taking some of the help from @Stimsoni :
// assumed all the variables are declared globally;
public boolean onTouch(View view, MotionEvent event) {
int MAX_CLICK_DURATION = 400;
int MAX_CLICK_DISTANCE = 5;
switch (event.getAction())
{
case MotionEvent.ACTION_DOWN: {
long clickDuration1 = Calendar.getInstance().getTimeInMillis() - startClickTime;
startClickTime = Calendar.getInstance().getTimeInMillis();
x1 = event.getX();
y1 = event.getY();
break;
}
case MotionEvent.ACTION_UP:
{
long clickDuration = Calendar.getInstance().getTimeInMillis() - startClickTime;
x2 = event.getX();
y2 = event.getY();
dx = x2-x1;
dy = y2-y1;
if(clickDuration < MAX_CLICK_DURATION && dx < MAX_CLICK_DISTANCE && dy < MAX_CLICK_DISTANCE) {
Toast.makeText(getApplicationContext(), "item clicked", Toast.LENGTH_SHORT).show();
Log.d("clicked", "On Item Clicked:: ");
// imageClickAction((ImageView) view,rl);
}
}
case MotionEvent.ACTION_MOVE:
long clickDuration = Calendar.getInstance().getTimeInMillis() - startClickTime;
x2 = event.getX();
y2 = event.getY();
dx = x2-x1;
dy = y2-y1;
if(clickDuration < MAX_CLICK_DURATION && dx < MAX_CLICK_DISTANCE && dy < MAX_CLICK_DISTANCE) {
//Toast.makeText(getApplicationContext(), "item clicked", Toast.LENGTH_SHORT).show();
// Log.d("clicked", "On Item Clicked:: ");
// imageClickAction((ImageView) view,rl);
}
else {
ClipData clipData = ClipData.newPlainText("", "");
View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(view);
//Toast.makeText(getApplicationContext(), "item dragged", Toast.LENGTH_SHORT).show();
view.startDrag(clipData, shadowBuilder, view, 0);
}
break;
}
return false;
}
Using Gil SH answer, I improved it by implementing onSingleTapUp()
rather than onSingleTapConfirmed()
. It is much faster and won't click the view if dragged/moved.
GestureTap:
public class GestureTap extends GestureDetector.SimpleOnGestureListener {
@Override
public boolean onSingleTapUp(MotionEvent e) {
button.performClick();
return true;
}
}
Use it like:
final GestureDetector gestureDetector = new GestureDetector(getApplicationContext(), new GestureTap());
button.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
gestureDetector.onTouchEvent(event);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
return true;
case MotionEvent.ACTION_UP:
return true;
case MotionEvent.ACTION_MOVE:
return true;
}
return false;
}
});