Check which direction was flinged in a Motion event android - android

So i want to detect which direction the user moved his finger when touching the screen
Right now its working for 3 directions but the "up" movement does not get called.
This is my code:
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
// store the X value when the user's finger was pressed down
downXValue = event.getX();
downYValue = event.getY();
break;
}
case MotionEvent.ACTION_UP: {
// Get the X value when the user released his/her finger
float currentX = event.getX();
float currentY = event.getY();
//check if horizontal or vertical movement was bigger
if (Math.abs(downXValue - currentX) > Math.abs(downYValue)
- currentY) {
Log.e("motionevent", "x");
// going backwards: pushing stuff to the right
if (downXValue < currentX) {
Log.e("motionevent", "right");
}
// going forwards: pushing stuff to the left
if (downXValue > currentX) {
Log.e("motionevent", "left");
}
} else {
Log.e("motionevent", "y");
if (downYValue < currentY) {
Log.e("motionevent", "up");
}
if (downYValue > currentY) {
Log.e("motionevent", "down");
}
}
break;
}
}
return true;
}
Is there a Problem with checking for horizontal or vertical movement? because whenever i do an up movement, right or left gets called. down works fine.

You have error in your movement calculation. I have fixed it, its ok now.
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
// store the X value when the user's finger was pressed down
downXValue = event.getX();
downYValue = event.getY();
Log.v("", "= " + downYValue);
break;
}
case MotionEvent.ACTION_UP: {
// Get the X value when the user released his/her finger
float currentX = event.getX();
float currentY = event.getY();
// check if horizontal or vertical movement was bigger
if (Math.abs(downXValue - currentX) > Math.abs(downYValue
- currentY)) {
Log.v("", "x");
// going backwards: pushing stuff to the right
if (downXValue < currentX) {
Log.v("", "right");
}
// going forwards: pushing stuff to the left
if (downXValue > currentX) {
Log.v("", "left");
}
} else {
Log.v("", "y ");
if (downYValue < currentY) {
Log.v("", "down");
}
if (downYValue > currentY) {
Log.v("", "up");
}
}
break;
}
}

You can use GestureDetector.OnGestureListener interface which provides several methods to detect touch events: scroll, fling, etc.
Usage:
#Override
public boolean onTouchEvent(MotionEvent event) {
if (detector == null) {
detector = new GestureDetector(this);
}
return detector.onTouchEvent(event);
}
From now every events are recognized and passed to the specified method.

Related

Gesture- volume control

i am trying to implement volume control using onTouchListener.
which i did nicely, but the problem is the volume gets increased by just 1 step.
For example: Total volume is 100 and steps are 10,20,30,40.. so on, it will only increase it 10 --NO MATTER HOW MANY TIMES I SWIPE THE LAYOUT UP--
but the volume down is super INSTANT, slight touch --GOES TO ZERO LIKE BANANAS--
#Override
public boolean onTouch(View v, MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_DOWN || event.getAction() == MotionEvent.ACTION_UP)
Log.i(TAG, "Action :" + event.getAction() + "\t X :" + event.getRawX() + "\t Y :"+ event.getRawY());
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
downY = event.getY();
case MotionEvent.ACTION_UP:
downY = event.getY();
case MotionEvent.ACTION_MOVE:
float y2 = event.getY();
diffY = (long) (Math.ceil(event.getY() - downY));
//if (Math.abs(diffY) > Math.abs(diffX)) {
if (downY < y2) {
//down swipe volume decrease
// audioManager.adjustVolume(AudioManager.ADJUST_LOWER, AudioManager.FLAG_PLAY_SOUND);
audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, AudioManager.ADJUST_LOWER, AudioManager.FLAG_PLAY_SOUND);
} else if (downY > y2) {
//up swipe volume increase
// audioManager.adjustVolume(AudioManager.ADJUST_RAISE, AudioManager.FLAG_PLAY_SOUND);
audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, AudioManager.ADJUST_RAISE, AudioManager.FLAG_PLAY_SOUND);
}
// }
}
return true;
}
I fixed it changing setStreamVolume to adjustStreamVolume.

finding the right direction while moving your finger in android devices

I'm currently processing movement on Android and I used MotionEvent.ACTION_MOVE.
I calculate currentx =(int) event.getX()/size and currenty=event.gety()/size, which gives me the integer base on the size of grid which I work on. Howevor, I want a way to find where the current y is changed while moving and decide to do something after that.
For example, if the size is equal to 10 then in x=150 and y=150 my currentx would be 15 and and currenty would be 15 but when currenty changed to something like 16 that means 150<event.get(y)<160. I want to know I'm in down direction etcetera.
public boolean onTouchEvent(MotionEvent event) {
//mGestureDetector.onTouchEvent(event);
int action = event.getActionMasked();
switch (action) {
case MotionEvent.ACTION_DOWN:
initialX = event.getX();
initialY = event.getY();
Log.d(TAG, "Action was DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.d(TAG, "Action was MOVE");
break;
case MotionEvent.ACTION_UP:
float finalX = event.getX();
float finalY = event.getY();
Log.d(TAG, "Action was UP");
if (initialX < finalX) {
Log.d(TAG, "Left to Right swipe performed");
}
if (initialX > finalX) {
Log.d(TAG, "Right to Left swipe performed");
}
if (initialY < finalY) {
Log.d(TAG, "Up to Down swipe performed");
}
if (initialY > finalY) {
Log.d(TAG, "Down to Up swipe performed");
}
break;
case MotionEvent.ACTION_CANCEL:
Log.d(TAG,"Action was CANCEL");
break;
case MotionEvent.ACTION_OUTSIDE:
Log.d(TAG, "Movement occurred outside bounds of current screen element");
break;
}
return super.onTouchEvent(event);
}
here is my code
private int getDirection(float startx, float starty,float endx ,float endy )
{
int direction=-1;
startx = (((int) startx / _size) *_size) ;
starty = (((int) starty / _size) * _size) ;
endx = (((int) endx / _size) * _size) ;
endy = (((int) endy / _size) * _size) ;
// Log.i("direction","startx:"+startx+"endx:"+endx+"starty: "+starty+"endy: "+endy+" ");
if (starty==endy)
{
direction= 0;
return direction;
}
if(startx==endx)
{
direction =1;
return direction;
}
if(Math.abs(startx-endx)==Math.abs(starty-endy))
{
direction =2;
return direction;
}
else
{
direction =-1;
return direction;
}
}
so i can find i'm in horizantal or vertical or non of these 2 cordinates but i need a way to compare the previous and current orientation while in action.move some thing like onDirectionChanggeListener
i can use action_up but i need it during move action.

Touch movement in only four directions?

I'm doing a simple match-three game, similar to Bejeweled and I just want to move sprite objects by touching the sprite object and then move it one step in four directions like left, right, up and down. I do this by comparing the X and Y values on Down with the X and Y values on Move. It's working but it's far from perfect! It's so easy to get a wrong value if the movement isn't straight. My questions is: is there a way to improve this and make it better?
I have also looked at gesture, but this seems very complicated to use with a surfaceview that I have.
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.i("test","Down");
touchActionDownX = (int)event.getX();
touchActionDownY = (int)event.getY();
touchActionMoveStatus = true;
gameLoop.touchX = (int)event.getX();
gameLoop.touchY = (int)event.getY();
gameLoop.touchActionDown = true;
break;
case MotionEvent.ACTION_POINTER_UP:
touchActionMoveStatus = true;
break;
case MotionEvent.ACTION_MOVE:
//Log.i("test","Move");
gameLoop.touchActionMove = true;
if(touchActionMoveStatus) {
touchActionMoveX = (int)event.getX();
touchActionMoveY = (int)event.getY();
if(touchActionMoveX < touchActionDownX)
Log.i("test","Move Left");
else if(touchActionMoveX > touchActionDownX)
Log.i("test","Move Right");
else if(touchActionMoveY < touchActionDownY)
Log.i("test","Move Up");
else if(touchActionMoveY > touchActionDownY)
Log.i("test","Move Down");
touchActionMoveStatus = false; // Will be set to true when pointer is up
}
break;
}
// return false;
return true; // This gets the coordinates all the time
}
Try something like this:
#Override
public boolean onTouch(View v, MotionEvent event) {
//You may have to play with the value and make it density dependant.
int threshold = 10;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.i("test","Down");
touchActionDownX = (int)event.getX();
touchActionDownY = (int)event.getY();
touchActionMoveStatus = true;
gameLoop.touchX = (int)event.getX();
gameLoop.touchY = (int)event.getY();
gameLoop.touchActionDown = true;
break;
case MotionEvent.ACTION_POINTER_UP:
touchActionMoveStatus = false;
break;
case MotionEvent.ACTION_MOVE:
//Log.i("test","Move");
gameLoop.touchActionMove = true;
if(touchActionMoveStatus) {
touchActionMoveX = (int)event.getX();
touchActionMoveY = (int)event.getY();
if(touchActionMoveX < (touchActionDownX - threshold) && (touchActionMoveY > (touchActionDownY - threshold)) && (touchActionMoveY (touchActionDownY + threshold))){
Log.i("test","Move Left");//If the move left was greater than the threshold and not greater than the threshold up or down
touchActionMoveStatus = false;
}
else if(touchActionMoveX > (touchActionDownX + threshold) && (touchActionMoveY > (touchActionDownY - threshold)) && (touchActionMoveY < (touchActionDownY + threshold))){
Log.i("test","Move Right");//If the move right was greater than the threshold and not greater than the threshold up or
touchActionMoveStatus = false;
}
else if(touchActionMoveY < (touchActionDownY - threshold) && (touchActionMoveX > (touchActionDownX - threshold)) && (touchActionMoveX < (touchActionDownX + threshold))){
Log.i("test","Move Up");//If the move up was greater than the threshold and not greater than the threshold left or right
touchActionMoveStatus = false;
}
else if(touchActionMoveY > (touchActionDownY + threshold) && (touchActionMoveX > (touchActionDownX - threshold)) && (touchActionMoveX < (touchActionDownX + threshold))){
Log.i("test","Move Down");//If the move down was greater than the threshold and not greater than the threshold left or right
touchActionMoveStatus = false;
}
}
break;
}
// return false;
return true; // This gets the coordinates all the time
}
Or use a ratio:
#Override
public boolean onTouch(View v, MotionEvent event) {
//You may have to play with the value.
//A value of two means you require the user to move twice as
//far in the direction they intend to move than any perpendicular direction.
float threshold = 2.0;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.i("test","Down");
touchActionDownX = (int)event.getX();
touchActionDownY = (int)event.getY();
touchActionMoveStatus = true;
gameLoop.touchX = (int)event.getX();
gameLoop.touchY = (int)event.getY();
gameLoop.touchActionDown = true;
break;
case MotionEvent.ACTION_POINTER_UP:
touchActionMoveStatus = true;
break;
case MotionEvent.ACTION_MOVE:
//Log.i("test","Move");
gameLoop.touchActionMove = true;
if(touchActionMoveStatus) {
touchActionMoveX = (int)event.getX();
touchActionMoveY = (int)event.getY();
// I haven't tested this so you may have a few typos to correct.
float ratioLeftRight = Math.abs(touchActionMoveX - touchActionDownX)/Math.abs(touchActionMoveY - touchActionDownY)
float ratioUpDown = Math.abs(touchActionMoveY - touchActionDownY)/Math.abs(touchActionMoveX - touchActionDownX)
if(touchActionMoveX < touchActionDownX && ratioLeftRight > threshold){
Log.i("test","Move Left");
touchActionMoveStatus = false;
}
else if(touchActionMoveX > touchActionDownX && ratioLeftRight > threshold){
Log.i("test","Move Right");
touchActionMoveStatus = false;
}
else if(touchActionMoveY < touchActionDownY && ratioUpDown > threshold){
Log.i("test","Move Up");
touchActionMoveStatus = false;
}
else if(touchActionMoveY > touchActionDownY && ratioUpDown > threshold){
Log.i("test","Move Down");
touchActionMoveStatus = false;
}
}
break;
}
// return false;
return true; // This gets the coordinates all the time
}
I would choose the dimension with the LARGEST movement and completely ignore the other, for example if the move is x=10 and y=8 then only use the x dimension (i.e. left/right) and vice versa.
Also as noted by Larry McKenzie, using a threshold to ignore smaller movements is a good idea to prevent registering accidental movements that the user did not intend. Tweak the threshold value to someting that feels natural.
Here is some code using your example (only the ACTION_MOVE case):
case MotionEvent.ACTION_MOVE:
//Log.i("test","Move");
gameLoop.touchActionMove = true;
if(touchActionMoveStatus) {
touchActionMoveX = (int)event.getX();
touchActionMoveY = (int)event.getY();
// setup a threshold (below which no movement would occur)
int threshold = 5; /* tweak this as needed */
// first calculate the "delta" movement amounts
int xMove = touchActionMoveX - touchActionDownX;
int yMove = touchActionMoveY - touchActionDownY;
// now find the largest of the two (note that if they
// are equal, x is assumed largest)
if ( Math.abs( xMove ) >= Math.abs( yMove ) ) { /* X-Axis */
if ( xMove >= threshold )
Log.i("test","Move Right");
else if ( xMove <= -threshold )
Log.i("test","Move Left");
}
else { /* Y-Axis */
if ( yMove >= threshold )
Log.i("test","Move Down");
else if ( yMove <= -threshold )
Log.i("test","Move Up");
}
touchActionMoveStatus = false; // Will be set to true when pointer is up
}
}
break;
NOTE: As mentioned in some of the other answers, because multiple events with very small values can occur, it might be best to accumulate (i.e. sum up) the movements UNTIL the threshold is reached - you can use members for this that reset in ACTION_DOWN. Once the threshold is reached (in either dimension) THEN you can perform the checks for which direction.
Alternative Approach
Another way to go about it would be to detect the largest movement in the FIRST ACTION_MOVE event, and then lock all further movements to that dimension. For this you would need to add various state members - these would need to be updated in each state.
Here is a rough example (with only the state tracking):
// members
private boolean axisLock = false; /* Track When Lock is Required */
private boolean axisX = true; /* Axis to Lock (true) for X, (false) for Y */
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// set this state so that ACTION_MOVE knows a lock is required
axisLock = true;
break;
case MotionEvent.ACTION_UP:
// clear the state in case no move was made
axisLock = false;
case MotionEvent.ACTION_MOVE:
// now lock the axis if this is the first move event
if ( axisLock ) {
// this will set whether the locked axis is X (true) or Y (false)
axisX = event.getX() >= event.getY();
// reset the state (to keep the axis locked)
axisLock = false;
}
// at this point you only need to consider the movement for the locked axis
if ( axisX ) {
int movement = (int)event.getX(); /* Get Movement for Locked Axis */
// check for your movement conditions here
}
else {
int movement = (int)event.getY(); /* Get Movement for Locked Axis */
// check for your movement conditions here
}
break;
}
return true;
}
You could add many optimizations to this code, for now it just illustrates the basic idea.
larry had the right idea, i just want to put in a lil fix,
//put this in the wraping class
private static int THRESHOLD = 10;
private static int initX;
private static int initY;
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
initX = (int)event.getX();
initY = (int)event.getY();
break;
case MotionEvent.ACTION_POINTER_UP:
//you can add in some kind of "move back" animation for the item
break;
case MotionEvent.ACTION_MOVE:
if(((int)event.getY - initY) > THRESHOLD){
//move down
break;
}
if(((int)event.getY - initY) > -THRESHOLD){
//move up
break;
}
if(((int)event.getX - initX) > THRESHOLD){
//move right
break;
}
if(((int)event.getX - initX) < -THRESHOLD){
//move left
break;
}
break;
}
}
i didn't test this code, only free write it, but i hope you get my idea :)

on touch even my code give always right side position

I'm developing an Android application and I would like to know if is possible to detect the direction of movement with one axis fixed. For example, I want put my phone on the table and detect the direction when I move it (left, right, up and down). The distance is not necessary, I just want to know the accurate direction.
Thanks!
my code is:
arg1.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View arg0, MotionEvent event) {
float startX = 0;
float startY;
float endX;
float endY;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
startX = (int) event.getX();
startY = (int) event.getY();
System.out.println("startX" + startX);
System.out.println("startY" + startY);
break;
case MotionEvent.ACTION_MOVE:
endX = event.getX();
endY = (int) event.getY();
System.out.println("endX" + endX);
System.out.println("endY" + endY);
float sub = endX - startX;
System.out.println("sub" + sub);
if ((endX - startX) < 100) {
arg0.setBackgroundColor(Color.GREEN);
}
if ((endX - startX) > 100) {
try {
TextView ph_tv = (TextView) arg0
.findViewById(R.id.textnum);
String Pho_no = ph_tv.getText().toString();
arg0.setBackgroundColor(Color.RED);
Intent intent = new Intent(
Intent.ACTION_CALL, Uri
.parse("tel:" + Pho_no));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
lv.setBackgroundColor(Color.BLACK);
} catch (ActivityNotFoundException e) {
Toast.makeText(
getApplicationContext(),
"Error in your phone call"
+ e.getMessage(),
Toast.LENGTH_LONG).show();
}
section(path_name, lv1);
}
break;
case MotionEvent.ACTION_UP:
endX = (int) event.getX();
endY = (int) event.getY();
}
return true;
}
});
If you want to detect finger movement direction I think using onTouchListener for your case is not a good solution. I suggest you to use gesture listener
this is the example for left and right
class MyGestureDetector extends SimpleOnGestureListener {
#Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
try {
if (Math.abs(e1.getY() - e2.getY()) > SWIPE_MAX_OFF_PATH)
return false;
// right to left swipe
if(e1.getX() - e2.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
Toast.makeText(SelectFilterActivity.this, "Left Swipe", Toast.LENGTH_SHORT).show();
} else if (e2.getX() - e1.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
Toast.makeText(SelectFilterActivity.this, "Right Swipe", Toast.LENGTH_SHORT).show();
}
} catch (Exception e) {
// nothing
}
return false;
}
}
you can do the same for up and down with calculating Y distance
If you have any other question about my answer or if you feel my answer isn't clear enough feel free to leave the question in the comment :)
I hope my answer helps you!
private final int mDragInterwell = 5;
private Point mStartPoint;
private final
#Override
public boolean onTouch(View arg0, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mStartPoint = new Point(event.getX(), event.getY());
break;
case MotionEvent.ACTION_MOVE:
final Point current = new Point(event.getX(), event.getY());
Log.d(getClass().getName(), decideMovement(current));
break;
case MotionEvent.ACTION_UP:
break;
}
return true;
}
private String decideMovement(final Point point)
{
String direction = "invalid";
boolean changed = false;
if( ( mStartPoint.x - point.x ) > mDragInterwell )
{
direction = "left";
changed = true;
}else if( ( mStartPoint.x - point.x ) > -mDragInterwell )
{
changed = true;
direction = "right";
}else if( ( mStartPoint.y - point.y ) > mDragInterwell )
{
changed = true;
direction = "down";
}else if( ( mStartPoint.y - point.y ) > -mDragInterwell )
{
changed = true;
direction = "up";
}
if(changed)
{
mStartPoint = point;
}
return direction;
}
Hope this will help..

Motionevent Action_MOVE keeps firing X and Y even if not moved

I am trying out simple program that have sounds based if moved. so at the beginning I have down - which play sound 1 and from then every move it is keeps playing a sound. At count 4 I have made it to play from start.
here is the problem: When I don't move my finger and hold it in the same place the sound still keeps 1 by 1 - Figured out the x and y value firing. How do I stop this??
OnTouchListener MyOnTouchListener= new OnTouchListener()
{
public boolean onTouch(View view, MotionEvent event)
{
switch(event.getAction() & MotionEvent.ACTION_MASK)
{
case MotionEvent.ACTION_DOWN:
x = (int) event.getX();
y = (int) event.getY();
oldval = x+y;
break;
case MotionEvent.ACTION_MOVE:
{
Log.e("X value", "X is "+x);
Log.e("Y value", "Y is "+y);
try
{
Thread.sleep(500);
} catch (InterruptedException e) {
}
int newval= (int) (event.getX() + event.getY());
if(Math.abs(oldval-newval)>50)
{
Log.e("First", "next button");
longpressCount++;
if(longpressCount==1)
{
Log.e("1", "BUTTON PRESSED");
}
else if(longpressCount==2)
{
Log.e("2", "BUTTON PRESSED");
}
else if(longpressCount==3)
{
Log.e("3", "BUTTON PRESSED");
}
else if(longpressCount==4)
{
Log.e("4", "BUTTON PRESSED");
longpressCount = 0;
}
}
break;
}
}
return true;
}
MOVE is very sensitive and will continue to be called as long as your finger is down. Set Old Value at the end of the sound playing code so it will only play if moved another 50 distance from that spot.
Something like this.
OnTouchListener MyOnTouchListener= new OnTouchListener()
{
public boolean onTouch(View view, MotionEvent event)
{
switch(event.getAction() & MotionEvent.ACTION_MASK)
{
case MotionEvent.ACTION_DOWN:
x = (int) event.getX();
y = (int) event.getY();
oldval = x+y;
break;
case MotionEvent.ACTION_MOVE:
{
Log.e("X value", "X is "+x);
Log.e("Y value", "Y is "+y);
try
{
Thread.sleep(500);
} catch (InterruptedException e) {
}
int newval= (int) (event.getX() + event.getY());
if(Math.abs(oldval-newval)>50)
{
Log.e("First", "next button");
longpressCount++;
if(longpressCount==1)
{
Log.e("1", "BUTTON PRESSED");
}
else if(longpressCount==2)
{
Log.e("2", "BUTTON PRESSED");
}
else if(longpressCount==3)
{
Log.e("3", "BUTTON PRESSED");
}
else if(longpressCount==4)
{
Log.e("4", "BUTTON PRESSED");
longpressCount = 0;
}
oldval = event.getX() + event.getY();
}
break;
}
}
return true;
}

Categories

Resources