Cómo detectar la dirección de desplazamiento entre izquierda / derecha y arriba / abajo

Mi pregunta: ¿Cómo puedo detectar cuándo un usuario mueve su dedo arriba / abajo vs izquierda / derecha (y cómo sé qué dirección de los grupos movió su dedo)?

Mi situación: Quiero cambiar el brillo de mi aplicación cuando mueven el dedo hacia arriba y hacia abajo (arriba = más brillante, abajo = más oscuro) y quiero cambiar entre actividades y / o vistas en función de su desplazamiento izquierdo / derecho.

Simplemente tiene que extender la clase SimpleOnGestureListener,

Declare esto en su clase,

private static final int SWIPE_MIN_DISTANCE = 120; private static final int SWIPE_MAX_OFF_PATH = 250; private static final int SWIPE_THRESHOLD_VELOCITY = 200; 

Como un ejemplo de desplazamiento horizontal puede ver el código a continuación,

  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) { onLeftSwipe(); } // left to right swipe else if (e2.getX() - e1.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) { onRightSwipe(); } } catch (Exception e) { } return false; } } 

Puede hacer esto de forma similar para el propósito de deslizar vertical.

Escribí una clase simple para esto: está bien documentado, así que no lo voy a explicar aquí

 public class OnSwipeListener extends GestureDetector.SimpleOnGestureListener { @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { // Grab two events located on the plane at e1=(x1, y1) and e2=(x2, y2) // Let e1 be the initial event // e2 can be located at 4 different positions, consider the following diagram // (Assume that lines are separated by 90 degrees.) // // // \ A / // \ / // D e1 B // / \ // / C \ // // So if (x2,y2) falls in region: // A => it's an UP swipe // B => it's a RIGHT swipe // C => it's a DOWN swipe // D => it's a LEFT swipe // float x1 = e1.getX(); float y1 = e1.getY(); float x2 = e2.getX(); float y2 = e2.getY(); Direction direction = getDirection(x1,y1,x2,y2); return onSwipe(direction); } /** Override this method. The Direction enum will tell you how the user swiped. */ public boolean onSwipe(Direction direction){ return false; } /** * Given two points in the plane p1=(x1, x2) and p2=(y1, y1), this method * returns the direction that an arrow pointing from p1 to p2 would have. * @param x1 the x position of the first point * @param y1 the y position of the first point * @param x2 the x position of the second point * @param y2 the y position of the second point * @return the direction */ public Direction getDirection(float x1, float y1, float x2, float y2){ double angle = getAngle(x1, y1, x2, y2); return Direction.get(angle); } /** * * Finds the angle between two points in the plane (x1,y1) and (x2, y2) * The angle is measured with 0/360 being the X-axis to the right, angles * increase counter clockwise. * * @param x1 the x position of the first point * @param y1 the y position of the first point * @param x2 the x position of the second point * @param y2 the y position of the second point * @return the angle between two points */ public double getAngle(float x1, float y1, float x2, float y2) { double rad = Math.atan2(y1-y2,x2-x1) + Math.PI; return (rad*180/Math.PI + 180)%360; } public enum Direction{ up, down, left, right; /** * Returns a direction given an angle. * Directions are defined as follows: * * Up: [45, 135] * Right: [0,45] and [315, 360] * Down: [225, 315] * Left: [135, 225] * * @param angle an angle from 0 to 360 - e * @return the direction of an angle */ public static Direction get(double angle){ if(inRange(angle, 45, 135)){ return Direction.up; } else if(inRange(angle, 0,45) || inRange(angle, 315, 360)){ return Direction.right; } else if(inRange(angle, 225, 315)){ return Direction.down; } else{ return Direction.left; } } /** * @param angle an angle * @param init the initial bound * @param end the final bound * @return returns true if the given angle is in the interval [init, end). */ private static boolean inRange(double angle, float init, float end){ return (angle >= init) && (angle < end); } } } 

Para usar simplemente extender OnSwipeListener y anular el método onSwipe

La respuesta de Fernandour es perfecta, estoy escribiendo esta respuesta sobre cómo usarla con Activity y Fragment como mucha gente lo está buscando.

 public class MyActivity extends Activity implements View.OnTouchListener{ private RelativeLayout someLayout; //take any layout on which you want your gesture listener; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); gestureDetector=new GestureDetector(this,new OnSwipeListener(){ @Override public boolean onSwipe(Direction direction) { if (direction==Direction.up){ //do your stuff Log.d(TAG, "onSwipe: up"); } if (direction==Direction.down){ //do your stuff Log.d(TAG, "onSwipe: down"); } return true; } }); someLayout.setOnTouchListener(this); } @Override public boolean onTouch(View v, MotionEvent event) { Log.d(TAG, "onTouch: "); gestureDetector.onTouchEvent(event); return true; } } 

Dar un ejemplo de uso para fernandohur respuesta anterior:

Si desea aplicar OnSwipeListener a una de sus vistas, así:
Dondequiera que esté esta vista – establezca un oyente táctil para esa vista, como esto:

 myview.setOnTouchListener(this); 

Ahora en OnCreate de su actividad o en su constructor de vista personalizada, haga lo siguiente:

 // Global private GestureDetectorCompat detector; // In OnCreate or custome view constructor (which extends one of Android views) detector = new GestureDetectorCompat(context, onSwipeListener); 

Anula en la misma clase el evento onTouch, así:

 @Override public boolean onTouch(View view, MotionEvent motionEvent) { return detector.onTouchEvent(motionEvent); } 

Y también tienen en la misma clase este objeto oyente:

 OnSwipeListener onSwipeListener = new OnSwipeListener() { @Override public boolean onSwipe(Direction direction) { // Possible implementation if(direction == Direction.left|| direction == Direction.right) { // Do something COOL like animation or whatever you want // Refer to your view if needed using a global reference return true; } else if(direction == Direction.up|| direction == Direction.down) { // Do something COOL like animation or whatever you want // Refer to your view if needed using a global reference return true; } return super.onSwipe(direction); } 

};

Tengo una biblioteca de gestos de código abierto en bitbucket dentro de esta biblioteca es una clase 'HGFling'. Esto demuestra cómo detectar la dirección de un fling. Básicamente, registra el XY del evento de gesto hacia abajo y el XY del evento ascendente y divídalos como una proporción luego usa la multiplicación simple para detectar el final de la animación. Puede descargar la biblioteca desde: https://bitbucket.org/warwick/hacergestov1 . Es de código abierto.

Aquí está el fragmento de código de la clase mencionada que hace el truco que está completamente probado y funciona perfectamente.

 public HGResult getUp(MotionEvent event) { if(disableGestureDynamically == true) { return hgResult; } if(disableGestureInternally == true) { return hgResult; } //Check flingTimeThreashold if(System.currentTimeMillis() < gestureStartTime + flingTimeThreashold) { hgResult.setTwoFingerDistance(hgResult.getTwoFingerDistance(firstTouchX, firstTouchY, event.getRawX(), event.getRawY())); //Check flingDistanceThreashold if(hgResult.getTwoFingerDistance() > flingDistanceThreashold) { paddingL = imageView.getPaddingLeft(); paddingT = imageView.getPaddingTop(); paddingR = imageView.getPaddingRight(); paddingB = imageView.getPaddingBottom(); leftRelativeToPadding = imageView.getLeft() + paddingL; topRelativeToPadding = imageView.getTop() + paddingT; rightRelativeToPadding = imageView.getMeasuredWidth() - paddingR; bottomRelativeToPadding = imageView.getMeasuredHeight() - paddingB; widthToHEightFlingRatio = Math.abs(firstTouchY - event.getRawY()) / Math.abs(firstTouchX - event.getRawX()); float animationStartX = matrixVals[Matrix.MTRANS_X]; float animationEndX = 0; float animationStartY = matrixVals[Matrix.MTRANS_Y]; float animationEndY = 0; if(dynamicFlingDistance == 0) {//This block is for default fling behaviour flings to the nearest edge. if(firstTouchX < event.getRawX()) {//Fling to right if(firstTouchY < event.getRawY()) {//Fling right & down if((point.x - leftRelativeToPadding - imageView.getLeft()) * widthToHEightFlingRatio < point.x) { //Right edge is closest animationEndX = imageView.getMeasuredWidth() - paddingL; animationEndY = (int) ((imageView.getBottom() - topRelativeToPadding) * widthToHEightFlingRatio); } else { //Bottom edge is closest animationEndX = (int) ((imageView.getRight() - leftRelativeToPadding) / widthToHEightFlingRatio); animationEndY = imageView.getMeasuredHeight() - paddingT; }//End if((point.x - leftRelativeToPadding - imageView.getLeft()) * widthToHEightFlingRatio < point.x) } else /*if(firstTouchY > releaseY)*/ {//Fling right & up if((point.x - leftRelativeToPadding - imageView.getLeft()) * widthToHEightFlingRatio < point.x) { //Right edge is closest animationEndX = imageView.getMeasuredWidth() - paddingL; animationEndY = (int) -((imageView.getBottom() - topRelativeToPadding) * widthToHEightFlingRatio); } else { //Top edge is closest animationEndX = (int) ((imageView.getRight() - leftRelativeToPadding) / widthToHEightFlingRatio); animationEndY = -(imageView.getMeasuredHeight() - paddingT); }//End if((point.x - leftRelativeToPadding - imageView.getLeft()) * widthToHEightFlingRatio < point.x) }//End if(firstTouchY < event.getRawY()) } else /*if(firstTouchX > releaseX)*/ {//Fling to left if(firstTouchY < event.getRawY()) {//Fling left down if((point.x - leftRelativeToPadding - imageView.getLeft()) * widthToHEightFlingRatio < point.x) { //Left edge is closest animationEndX = -(imageView.getMeasuredWidth() - paddingL); animationEndY = (int) ((imageView.getBottom() - topRelativeToPadding) * widthToHEightFlingRatio); } else { //Bottom edge is closest animationEndX = (int) - ((imageView.getRight() - leftRelativeToPadding) / widthToHEightFlingRatio); animationEndY = imageView.getMeasuredHeight() - paddingT; }//End if((point.x - leftRelativeToPadding - imageView.getLeft()) * widthToHEightFlingRatio < point.x) } else /*if(firstTouchY > releaseY)*/ {//Fling left up if((point.x - leftRelativeToPadding - imageView.getLeft()) * widthToHEightFlingRatio < point.x) { //Left edge is closest animationEndX = -(imageView.getMeasuredWidth() - paddingL); animationEndY = (int) -((imageView.getBottom() - topRelativeToPadding) * widthToHEightFlingRatio); } else { //Top edge is closest animationEndX = (int) - ((imageView.getRight() - leftRelativeToPadding) / widthToHEightFlingRatio); animationEndY = -(imageView.getMeasuredHeight() - paddingT); }//End if((point.x - leftRelativeToPadding - imageView.getLeft()) * widthToHEightFlingRatio < point.x) }//End if(firstTouchY < event.getRawY()) }//End if(firstTouchX < event.getRawX()) translateAnimation = new TranslateAnimation(animationStartX, animationEndX, animationStartY, animationEndY); imageView.setAnimation(translateAnimation); imageView.getAnimation().setDuration(flingAnimationTime); translateAnimation.setAnimationListener(animationListener); imageView.startAnimation(translateAnimation); if(bounceBack == false) { final float animationEndXFinal = animationEndX; final float animationEndYFinal = animationEndY; new Handler().postDelayed( new Runnable() { @Override public void run() { matrixVals[Matrix.MTRANS_X] = animationEndXFinal; matrixVals[Matrix.MTRANS_Y] = animationEndYFinal; matrix.setValues(matrixVals); imageView.setImageMatrix(matrix); imageView.invalidate(); imageView.requestLayout(); } }, flingAnimationTime); }//End if(bounceBack == false) } else /*if(dynamicFlingDistance != 0)*/ {//condition that is called when setFlingDistance(int dynamicFlingDistance) method sets a non zero value triggerFling(dynamicFlingDistance, event.getRawX() - firstTouchX, event.getRawY() - firstTouchY); }//End if(dynamicFlingDistance == 0) }//End if(hgResult.getTwoFingerDistance() > flingDistanceThreashold) }//End if(System.currentTimeMillis() < gestureStartTime + flingTimeThreashold) hgResult.setFirstTouchX(event.getRawX()); hgResult.setFirstTouchY(event.getRawY()); hgResult.setMoveDistanceX(event.getRawX() - firstTouchX); hgResult.setMoveDistanceY(event.getRawY() - firstTouchY); return hgResult; }//End HGResult getUp(MotionEvent event) 

He resuelto de esta manera:

 viewPager.setOnTouchListener(new View.OnTouchListener() { float prevX = -1; @Override public boolean onTouch(View v, MotionEvent event) { if (prevX != -1) { if (event.getX() > prevX) { if (viewPager.getCurrentItem() == 0) { // Left to Right swipe } //Log.d("DEBUG", MotionEvent.ACTION_MOVE + ":" + event.getAction() + ":" + event.getActionMasked() + ":Left Swipe" + ":" + prevX + ":" + event.getX() + ":" + viewPager.getCurrentItem()); } else if (prevX > event.getX()) { // Right to left swipe //Log.d("DEBUG", MotionEvent.ACTION_MOVE + ":" + event.getAction() + ":" + event.getActionMasked() + ":Right Swipe" + ":" + prevX + ":" + event.getX() + ":" + viewPager.getCurrentItem()); } } if (event.getAction() == MotionEvent.ACTION_MOVE) { prevX = event.getX(); } else { prevX = -1; } return false; } }); 

Las respuestas disponibles son demasiado complejas para un problema tan simple. Sugiero otro acercamiento a él (el código es as3, pero usted puede conseguir la idea):

 var touchDistance:Number = Point.distance(_moveTouchPoint, _startTouchPoint); if (touchDistance >= SWIPE_MIN_DISTANCE) { var xDiff:Number = _moveTouchPoint.x - _startTouchPoint.x; var yDiff:Number = _moveTouchPoint.y - _startTouchPoint.y; var yGreater:Boolean = Math.abs(yDiff) >= Math.abs(xDiff); if (yGreater) { // direction is up or down changePlayerDirectionTo(yDiff < 0 ? DIRECTION_UP : DIRECTION_DOWN); } else { // direction is left or right changePlayerDirectionTo(xDiff < 0 ? DIRECTION_LEFT : DIRECTION_RIGHT) } } 

En cada caso, x o y serán mayores en valor absoluto, lo que puede resolverse hasta cierto conjunto de direcciones. A partir de entonces puede confiar en el signo de coordenadas para detectar exactamente qué dirección.

  • Comprobar la configuración del teléfono para la retroalimentación háptica
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.