Question Détection de gestes Fling sur la disposition de la grille


Je veux obtenir fling détection de gestes fonctionnant dans mon application Android.

Ce que j'ai est un GridLayout qui contient 9 ImageViews. La source peut être trouvée ici: Grid Layout de Romain Guys.

Ce dossier que je prends vient de Romain Guy Application Photostream et a seulement été légèrement adapté.

Pour la situation de clic simple, je n'ai besoin que de onClickListener pour chaque ImageView J'ajoute à être le principal activity qui implémente View.OnClickListener. Il semble infiniment plus compliqué de mettre en œuvre quelque chose qui reconnaît un fling. Je présume que c'est parce qu'il peut s'étendre views?

  • Si mon activité met en œuvre OnGestureListener Je ne sais pas comment définir cela comme l'écouteur de gestes pour la Grid ou la Image vues que je ajouter.

    public class SelectFilterActivity extends Activity implements
       View.OnClickListener, OnGestureListener { ...
    
  • Si mon activité met en œuvre OnTouchListener alors je n'ai pas onFling méthode pour override (il a deux événements en tant que paramètres me permettant pour déterminer si l'aventure était remarquable).

    public class SelectFilterActivity extends Activity implements
        View.OnClickListener, OnTouchListener { ...
    
  • Si je fais une coutume View, comme GestureImageView ça s'étend ImageView Je ne sais pas comment dire à l'activité qu'un fling a eu lieu à partir de la vue. En tout cas, j'ai essayé ceci et les méthodes n'ont pas été appelées quand j'ai touché l'écran.

J'ai juste besoin d'un exemple concret de ce travail à travers les vues. Quoi, quand et comment dois-je attacher listener? Je dois aussi pouvoir détecter les clics uniques.

// Gesture detection
mGestureDetector = new GestureDetector(this, new GestureDetector.SimpleOnGestureListener() {

    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
        int dx = (int) (e2.getX() - e1.getX());
        // don't accept the fling if it's too short
        // as it may conflict with a button push
        if (Math.abs(dx) > MAJOR_MOVE && Math.abs(velocityX) > Math.absvelocityY)) {
            if (velocityX > 0) {
                moveRight();
            } else {
                moveLeft();
            }
            return true;
        } else {
            return false;
        }
    }
});

Est-il possible de poser une vue transparente sur le haut de mon écran pour capturer des flings?

Si je choisis de ne pas inflate mes vues d'image enfant de XML puis-je passer le GestureDetector en tant que paramètre constructeur à une nouvelle sous-classe de ImageView que je crée?

C'est l'activité très simple que j'essaie d'obtenir fling détection pour travailler pour: SelectFilterActivity (Adapté de photostream).

J'ai regardé ces sources:

Rien n'a fonctionné pour moi jusqu'ici et j'espérais des indications.


997
2018-06-01 23:35


origine


Réponses:


Grâce à Code Shogun, dont j'ai adapté le code à ma situation.

Laissez votre activité se réaliserOnClickListener comme d'habitude:

public class SelectFilterActivity extends Activity implements OnClickListener {

  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;
  private GestureDetector gestureDetector;
  View.OnTouchListener gestureListener;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    /* ... */

    // Gesture detection
    gestureDetector = new GestureDetector(this, new MyGestureDetector());
    gestureListener = new View.OnTouchListener() {
      public boolean onTouch(View v, MotionEvent event) {
        return gestureDetector.onTouchEvent(event);
      }
    };

  }

  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;
    }

    @Override
    public boolean onDown(MotionEvent e) {
      return true;
    }
  }
}

Attachez votre écouteur gestuel à toutes les vues que vous ajoutez à la mise en page principale;

// Do this for each view added to the grid
imageView.setOnClickListener(SelectFilterActivity.this); 
imageView.setOnTouchListener(gestureListener);

Regardez en admiration lorsque vos méthodes surchargées sont touchées, à la fois le onClick(View v) de l'activité et le onFling de l'auditeur du geste.

public void onClick(View v) {
  Filter f = (Filter) v.getTag();
  FilterFullscreenActivity.show(this, input, f);
}

La danse "fling" est facultative mais encouragée.


774
2018-04-20 17:26



L'une des réponses ci-dessus mentionne la gestion de la densité de pixels différents, mais suggère de calculer les paramètres de balayage à la main. Il est à noter que vous pouvez réellement obtenir des valeurs raisonnables et à l'échelle du système en utilisant ViewConfiguration classe:

final ViewConfiguration vc = ViewConfiguration.get(getContext());
final int swipeMinDistance = vc.getScaledPagingTouchSlop();
final int swipeThresholdVelocity = vc.getScaledMinimumFlingVelocity();
final int swipeMaxOffPath = vc.getScaledTouchSlop();
// (there is also vc.getScaledMaximumFlingVelocity() one could check against)

J'ai remarqué que l'utilisation de ces valeurs rend la «sensation» de fling plus cohérente entre l'application et le reste du système.


203
2018-04-21 10:24



Je le fais un peu différent, et a écrit une classe de détecteur supplémentaire qui implémente le View.onTouchListener 

onCreateest simplement l'ajouter à la mise en page la plus basse comme ceci:

ActivitySwipeDetector activitySwipeDetector = new ActivitySwipeDetector(this);
lowestLayout = (RelativeLayout)this.findViewById(R.id.lowestLayout);
lowestLayout.setOnTouchListener(activitySwipeDetector);

où id.lowestLayout est l'id.xxx pour la vue la plus basse dans la hiérarchie de présentation et la propriété lowestLayout est déclarée comme RelativeLayout

Et puis il y a la classe de détecteur de balayage d'activité réelle:

public class ActivitySwipeDetector implements View.OnTouchListener {

static final String logTag = "ActivitySwipeDetector";
private Activity activity;
static final int MIN_DISTANCE = 100;
private float downX, downY, upX, upY;

public ActivitySwipeDetector(Activity activity){
    this.activity = activity;
}

public void onRightSwipe(){
    Log.i(logTag, "RightToLeftSwipe!");
    activity.doSomething();
}

public void onLeftSwipe(){
    Log.i(logTag, "LeftToRightSwipe!");
    activity.doSomething();
}

public void onDownSwipe(){
    Log.i(logTag, "onTopToBottomSwipe!");
    activity.doSomething();
}

public void onUpSwipe(){
    Log.i(logTag, "onBottomToTopSwipe!");
    activity.doSomething();
}

public boolean onTouch(View v, MotionEvent event) {
    switch(event.getAction()){
        case MotionEvent.ACTION_DOWN: {
            downX = event.getX();
            downY = event.getY();
            return true;
        }
        case MotionEvent.ACTION_UP: {
            upX = event.getX();
            upY = event.getY();

            float deltaX = downX - upX;
            float deltaY = downY - upY;

       // swipe horizontal?
        if(Math.abs(deltaX) > Math.abs(deltaY))
        {
            if(Math.abs(deltaX) > MIN_DISTANCE){
                // left or right
                if(deltaX > 0) { this.onRightSwipe(); return true; }
                if(deltaX < 0) { this.onLeftSwipe(); return true; }
            }
            else {
                    Log.i(logTag, "Horizontal Swipe was only " + Math.abs(deltaX) + " long, need at least " + MIN_DISTANCE);
                    return false; // We don't consume the event
            }
        }
        // swipe vertical?
        else 
        {
            if(Math.abs(deltaY) > MIN_DISTANCE){
                // top or down
                if(deltaY < 0) { this.onDownSwipe(); return true; }
                if(deltaY > 0) { this.onUpSwipe(); return true; }
            }
            else {
                    Log.i(logTag, "Vertical Swipe was only " + Math.abs(deltaX) + " long, need at least " + MIN_DISTANCE);
                    return false; // We don't consume the event
            }
        }

            return true;
        }
    }
    return false;
}

}

Fonctionne vraiment bien pour moi!


140
2018-01-10 16:11



J'ai légèrement modifié et réparé la solution de Thomas Fankhauser

Le système entier consiste en deux fichiers, SwipeInterface et ActivitySwipeDetector


SwipeInterface.java

import android.view.View;

public interface SwipeInterface {

    public void bottom2top(View v);

    public void left2right(View v);

    public void right2left(View v);

    public void top2bottom(View v);

}

Détecteur

import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

public class ActivitySwipeDetector implements View.OnTouchListener {

    static final String logTag = "ActivitySwipeDetector";
    private SwipeInterface activity;
    static final int MIN_DISTANCE = 100;
    private float downX, downY, upX, upY;

    public ActivitySwipeDetector(SwipeInterface activity){
        this.activity = activity;
    }

    public void onRightToLeftSwipe(View v){
        Log.i(logTag, "RightToLeftSwipe!");
        activity.right2left(v);
    }

    public void onLeftToRightSwipe(View v){
        Log.i(logTag, "LeftToRightSwipe!");
        activity.left2right(v);
    }

    public void onTopToBottomSwipe(View v){
        Log.i(logTag, "onTopToBottomSwipe!");
        activity.top2bottom(v);
    }

    public void onBottomToTopSwipe(View v){
        Log.i(logTag, "onBottomToTopSwipe!");
        activity.bottom2top(v);
    }

    public boolean onTouch(View v, MotionEvent event) {
        switch(event.getAction()){
        case MotionEvent.ACTION_DOWN: {
            downX = event.getX();
            downY = event.getY();
            return true;
        }
        case MotionEvent.ACTION_UP: {
            upX = event.getX();
            upY = event.getY();

            float deltaX = downX - upX;
            float deltaY = downY - upY;

            // swipe horizontal?
            if(Math.abs(deltaX) > MIN_DISTANCE){
                // left or right
                if(deltaX < 0) { this.onLeftToRightSwipe(v); return true; }
                if(deltaX > 0) { this.onRightToLeftSwipe(v); return true; }
            }
            else {
                Log.i(logTag, "Swipe was only " + Math.abs(deltaX) + " long, need at least " + MIN_DISTANCE);
            }

            // swipe vertical?
            if(Math.abs(deltaY) > MIN_DISTANCE){
                // top or down
                if(deltaY < 0) { this.onTopToBottomSwipe(v); return true; }
                if(deltaY > 0) { this.onBottomToTopSwipe(v); return true; }
            }
            else {
                Log.i(logTag, "Swipe was only " + Math.abs(deltaX) + " long, need at least " + MIN_DISTANCE);
                v.performClick();
            }
        }
        }
        return false;
    }

}

il est utilisé comme ceci:

ActivitySwipeDetector swipe = new ActivitySwipeDetector(this);
LinearLayout swipe_layout = (LinearLayout) findViewById(R.id.swipe_layout);
swipe_layout.setOnTouchListener(swipe);

Et en mettant en œuvre Activity vous devez mettre en œuvre des méthodes de SwipeInterface, et vous pouvez savoir sur lequel Voir le Swipe Event a été appelé.

@Override
public void left2right(View v) {
    switch(v.getId()){
        case R.id.swipe_layout:
            // do your stuff here
        break;
    }       
}

90
2018-02-18 09:45



Le code du détecteur de mouvement de balayage ci-dessus est très utile! Vous pouvez cependant souhaiter rendre cette solution agnostique en utilisant les valeurs relatives suivantes (REL_SWIPE) plutôt que les valeurs absolues (SWIPE_) 

DisplayMetrics dm = getResources().getDisplayMetrics();

int REL_SWIPE_MIN_DISTANCE = (int)(SWIPE_MIN_DISTANCE * dm.densityDpi / 160.0f);
int REL_SWIPE_MAX_OFF_PATH = (int)(SWIPE_MAX_OFF_PATH * dm.densityDpi / 160.0f);
int REL_SWIPE_THRESHOLD_VELOCITY = (int)(SWIPE_THRESHOLD_VELOCITY * dm.densityDpi / 160.0f);

61
2017-07-18 01:00



Ma version de la solution proposée par Thomas Fankhauser et Marek Sebera (ne gère pas les balayages verticaux):

SwipeInterface.java

import android.view.View;

public interface SwipeInterface {

    public void onLeftToRight(View v);

    public void onRightToLeft(View v);
}

ActivitySwipeDetector.java

import android.content.Context;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;

public class ActivitySwipeDetector implements View.OnTouchListener {

    static final String logTag = "ActivitySwipeDetector";
    private SwipeInterface activity;
    private float downX, downY;
    private long timeDown;
    private final float MIN_DISTANCE;
    private final int VELOCITY;
    private final float MAX_OFF_PATH;

    public ActivitySwipeDetector(Context context, SwipeInterface activity){
        this.activity = activity;
        final ViewConfiguration vc = ViewConfiguration.get(context);
        DisplayMetrics dm = context.getResources().getDisplayMetrics();
        MIN_DISTANCE = vc.getScaledPagingTouchSlop() * dm.density;
        VELOCITY = vc.getScaledMinimumFlingVelocity();
        MAX_OFF_PATH = MIN_DISTANCE * 2;            
    }

    public void onRightToLeftSwipe(View v){
        Log.i(logTag, "RightToLeftSwipe!");
        activity.onRightToLeft(v);
    }

    public void onLeftToRightSwipe(View v){
        Log.i(logTag, "LeftToRightSwipe!");
        activity.onLeftToRight(v);
    }

    public boolean onTouch(View v, MotionEvent event) {
        switch(event.getAction()){
        case MotionEvent.ACTION_DOWN: {
            Log.d("onTouch", "ACTION_DOWN");
            timeDown = System.currentTimeMillis();
            downX = event.getX();
            downY = event.getY();
            return true;
        }
        case MotionEvent.ACTION_UP: {
            Log.d("onTouch", "ACTION_UP");
            long timeUp = System.currentTimeMillis();
            float upX = event.getX();
            float upY = event.getY();

            float deltaX = downX - upX;
            float absDeltaX = Math.abs(deltaX); 
            float deltaY = downY - upY;
            float absDeltaY = Math.abs(deltaY);

            long time = timeUp - timeDown;

            if (absDeltaY > MAX_OFF_PATH) {
                Log.i(logTag, String.format("absDeltaY=%.2f, MAX_OFF_PATH=%.2f", absDeltaY, MAX_OFF_PATH));
                return v.performClick();
            }

            final long M_SEC = 1000;
            if (absDeltaX > MIN_DISTANCE && absDeltaX > time * VELOCITY / M_SEC) {
                if(deltaX < 0) { this.onLeftToRightSwipe(v); return true; }
                if(deltaX > 0) { this.onRightToLeftSwipe(v); return true; }
            } else {
                Log.i(logTag, String.format("absDeltaX=%.2f, MIN_DISTANCE=%.2f, absDeltaX > MIN_DISTANCE=%b", absDeltaX, MIN_DISTANCE, (absDeltaX > MIN_DISTANCE)));
                Log.i(logTag, String.format("absDeltaX=%.2f, time=%d, VELOCITY=%d, time*VELOCITY/M_SEC=%d, absDeltaX > time * VELOCITY / M_SEC=%b", absDeltaX, time, VELOCITY, time * VELOCITY / M_SEC, (absDeltaX > time * VELOCITY / M_SEC)));
            }

        }
        }
        return false;
    }

}

32
2018-04-23 19:54



Cette question est un peu vieux et en Juillet 2011 Google a publié le Compatibilité, révision 3) qui comprend le ViewPager cela fonctionne avec Android 1.6 vers le haut. le GestureListener réponses postées pour cette question ne vous sentez pas très élégant sur Android. Si vous recherchez le code utilisé pour basculer entre les photos de la Galerie Android ou pour changer de vue dans la nouvelle application Play Market, il est définitivement ViewPager.

Voici quelques liens pour plus d'informations:


21
2018-04-05 14:28



Il y a une interface intégrée que vous pouvez utiliser directement pour tous les gestes:
Voici une explication pour un utilisateur de niveau de base: enter image description here Il y a 2 importations faites attention en choisissant que les deux sont diferent enter image description here enter image description here


14
2018-04-12 21:14



Aussi comme amélioration mineure.

La principale raison du bloc try / catch est que e1 pourrait être nul pour le mouvement initial. en plus du try / catch, incluez un test pour null et return. similaire à la suivante

if (e1 == null || e2 == null) return false;
try {
...
} catch (Exception e) {}
return false;

11
2017-11-02 23:14