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
If you want to react on click only, use:
if (event.getAction() == MotionEvent.ACTION_UP) {
}
To get most optimized recognition of click event We have to consider 2 things:
Actually i combine the logic given by Stimsoni and Neethirajan
So here is my solution:
view.setOnTouchListener(new OnTouchListener() {
private final int MAX_CLICK_DURATION = 400;
private final int MAX_CLICK_DISTANCE = 5;
private long startClickTime;
private float x1;
private float y1;
private float x2;
private float y2;
private float dx;
private float dy;
@Override
public boolean onTouch(View view, MotionEvent event) {
// TODO Auto-generated method stub
switch (event.getAction())
{
case MotionEvent.ACTION_DOWN:
{
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)
Log.v("","On Item Clicked:: ");
}
}
return false;
}
});
Below code will solve your problem
@Override public boolean onTouchEvent(MotionEvent event) { switch(event.getAction()) { case(MotionEvent.ACTION_DOWN): x1 = event.getX(); y1 = event.getY(); break; case(MotionEvent.ACTION_UP): { x2 = event.getX(); y2 = event.getY(); dx = x2-x1; dy = y2-y1; if(Math.abs(dx) > Math.abs(dy)) { if(dx>0) move(1); //right else if (dx == 0) move(5); //click else move(2); //left } else { if(dy>0) move(3); // down else if (dy == 0) move(5); //click else move(4); //up } } } return true; }
I got the best results by taking into account:
ACTION_DOWN
and ACTION_UP
events. I wanted to specify the max allowed distance in density-indepenent pixels rather than pixels, to better support different screens. For example, 15 DP.Example:
/**
* Max allowed duration for a "click", in milliseconds.
*/
private static final int MAX_CLICK_DURATION = 1000;
/**
* Max allowed distance to move during a "click", in DP.
*/
private static final int MAX_CLICK_DISTANCE = 15;
private long pressStartTime;
private float pressedX;
private float pressedY;
@Override
public boolean onTouchEvent(MotionEvent e) {
switch (e.getAction()) {
case MotionEvent.ACTION_DOWN: {
pressStartTime = System.currentTimeMillis();
pressedX = e.getX();
pressedY = e.getY();
break;
}
case MotionEvent.ACTION_UP: {
long pressDuration = System.currentTimeMillis() - pressStartTime;
if (pressDuration < MAX_CLICK_DURATION && distance(pressedX, pressedY, e.getX(), e.getY()) < MAX_CLICK_DISTANCE) {
// Click event has occurred
}
}
}
}
private static float distance(float x1, float y1, float x2, float y2) {
float dx = x1 - x2;
float dy = y1 - y2;
float distanceInPx = (float) Math.sqrt(dx * dx + dy * dy);
return pxToDp(distanceInPx);
}
private static float pxToDp(float px) {
return px / getResources().getDisplayMetrics().density;
}
The idea here is the same as in Gem's solution, with these differences:
Update (2015): also check out Gabriel's fine-tuned version of this.
Here's another solution that is very simple and doesn't require you to worry about the finger being moved. If you are basing a click as simply the distance moved then how can you differentiate a click and a long click.
You could put more smarts into this and include the distance moved, but i'm yet to come across an instance when the distance a user can move in 200 milliseconds should constitute a move as opposed to a click.
setOnTouchListener(new OnTouchListener() {
private static final int MAX_CLICK_DURATION = 200;
private long startClickTime;
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
startClickTime = Calendar.getInstance().getTimeInMillis();
break;
}
case MotionEvent.ACTION_UP: {
long clickDuration = Calendar.getInstance().getTimeInMillis() - startClickTime;
if(clickDuration < MAX_CLICK_DURATION) {
//click event has occurred
}
}
}
return true;
}
});
Taking Jonik's lead I built a slightly more fine tuned version, that doesn't register as a click if you move your finger and then return to the spot before letting go:
So here is my solution:
/**
* Max allowed duration for a "click", in milliseconds.
*/
private static final int MAX_CLICK_DURATION = 1000;
/**
* Max allowed distance to move during a "click", in DP.
*/
private static final int MAX_CLICK_DISTANCE = 15;
private long pressStartTime;
private float pressedX;
private float pressedY;
private boolean stayedWithinClickDistance;
@Override
public boolean onTouchEvent(MotionEvent e) {
switch (e.getAction()) {
case MotionEvent.ACTION_DOWN: {
pressStartTime = System.currentTimeMillis();
pressedX = e.getX();
pressedY = e.getY();
stayedWithinClickDistance = true;
break;
}
case MotionEvent.ACTION_MOVE: {
if (stayedWithinClickDistance && distance(pressedX, pressedY, e.getX(), e.getY()) > MAX_CLICK_DISTANCE) {
stayedWithinClickDistance = false;
}
break;
}
case MotionEvent.ACTION_UP: {
long pressDuration = System.currentTimeMillis() - pressStartTime;
if (pressDuration < MAX_CLICK_DURATION && stayedWithinClickDistance) {
// Click event has occurred
}
}
}
}
private static float distance(float x1, float y1, float x2, float y2) {
float dx = x1 - x2;
float dy = y1 - y2;
float distanceInPx = (float) Math.sqrt(dx * dx + dy * dy);
return pxToDp(distanceInPx);
}
private static float pxToDp(float px) {
return px / getResources().getDisplayMetrics().density;
}