Frage Fling-Gestenerkennung im Grid-Layout


ich möchte bekommen fling Gestenerkennung funktioniert in meiner Android-Anwendung.

Was ich habe, ist ein GridLayout das enthält 9 ImageViews. Die Quelle kann hier gefunden werden: Romain Guys's Raster-Layout.

Diese Datei, die ich nehme, stammt von Romain Guy Fotostream-Anwendung und wurde nur geringfügig angepasst.

Für die einfache Klick - Situation muss ich nur die onClickListener für jede ImageView Ich füge hinzu, um das Haupt zu sein activity was implementiert View.OnClickListener. Es scheint unendlich komplizierter, etwas zu implementieren, das a erkennt fling. Ich nehme an, dass dies so ist, weil es sich überspannen kann views?

  • Wenn meine Aktivität implementiert OnGestureListener Ich weiß nicht wie setze das als Geste Listener für das Grid oder der Image sieht, dass ich hinzufügen.

    public class SelectFilterActivity extends Activity implements
       View.OnClickListener, OnGestureListener { ...
    
  • Wenn meine Aktivität implementiert OnTouchListener dann habe ich keine onFling Methode zu override (es hat zwei Ereignisse als Parameter, die mir erlauben um festzustellen, ob der Fling war bemerkenswert).

    public class SelectFilterActivity extends Activity implements
        View.OnClickListener, OnTouchListener { ...
    
  • Wenn ich eine Gewohnheit mache View, mögen GestureImageView das erstreckt sich ImageView Ich weiß nicht, wie ich der Aktivität sagen soll, dass a fling ist aus der Sicht aufgetreten. Auf jeden Fall habe ich das versucht und die Methoden wurden nicht aufgerufen, als ich den Bildschirm berührte.

Ich brauche nur ein konkretes Beispiel dafür, wie ich über Ansichten hinweg arbeiten kann. Was, wann und wie soll ich das anbringen? listener? Ich muss auch einzelne Klicks erkennen können.

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

Ist es möglich, eine transparente Sicht über den oberen Bildschirmrand zu werfen, um das Schlagen zu erfassen?

Wenn ich es nicht wähle inflate Meine Kindbildansichten aus XML kann ich weitergeben GestureDetector als Konstruktorparameter für eine neue Unterklasse von ImageView das ich erstelle?

Dies ist die sehr einfache Aktivität, die ich versuche zu bekommen fling Erkennung für: SelectFilterActivity (Angepasst von Fotostream).

Ich habe mir diese Quellen angesehen:

Bis jetzt hat noch nichts für mich funktioniert und ich hatte auf einige Hinweise gehofft.


997
2018-06-01 23:35


Ursprung


Antworten:


Dank an Code Shogun, dessen Code ich meiner Situation angepasst habe.

Lassen Sie Ihre Aktivität implementierenOnClickListener wie gewöhnlich:

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

Hängen Sie Ihren Gestenlistener an alle Ansichten an, die Sie dem Hauptlayout hinzufügen.

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

Beobachten Sie ehrfürchtig, wie Ihre überholten Methoden getroffen werden, sowohl die onClick(View v) der Aktivität und der onFling des Gestenhörers.

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

Der post 'fling' Tanz ist optional, aber erwünscht.


774
2018-04-20 17:26



Eine der obigen Antworten erwähnt die Handhabung unterschiedlicher Pixeldichte, schlägt jedoch vor, die Swipe-Parameter von Hand zu berechnen. Es ist erwähnenswert, dass Sie skalierte, vernünftige Werte aus dem System erhalten können ViewConfiguration Klasse:

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)

Mir ist aufgefallen, dass die Verwendung dieser Werte das "Gefühl" von Fling zwischen der Anwendung und dem Rest des Systems konsistenter macht.


203
2018-04-21 10:24



Ich mache es ein wenig anders, und schrieb eine zusätzliche Detektorklasse, die das implementiert View.onTouchListener 

onCreateFügen Sie es einfach zum untersten Layout hinzu:

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

Dabei ist id.lowestLayout die ID.xxx für die Ansicht in der Layouthierarchie und lowestLayout als RelativeLayout

Und dann gibt es die eigentliche Aktivität Swipe-Detektor-Klasse:

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

}

Funktioniert wirklich gut für mich!


140
2018-01-10 16:11



Ich habe die Lösung leicht modifiziert und repariert Thomas Fankhauser

Das ganze System besteht aus zwei Dateien, SwipeInterface und 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);

}

Detektor

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

}

Es wird so benutzt:

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

Und bei der Umsetzung Activity Sie müssen Methoden implementieren von SwipeInterfaceund Sie können herausfinden, auf welche Swipe-Ereignis hieß.

@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



Der obige Wischgesten-Detektorcode ist sehr nützlich! Sie können diese Lösungsdichte jedoch mit den folgenden relativen Werten agnostisch machen (REL_SWIPE) anstatt die absoluten Werte (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



Meine Version der Lösung vorgeschlagen von Thomas Fankhauser und Marek Sebera (behandelt nicht vertikale Swipes):

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



Diese Frage ist irgendwie alt und im Juli 2011 veröffentlichte Google die Kompatibilitätspaket, Revision 3) was beinhaltet die ViewPager das funktioniert mit Android 1.6 aufwärts. Das GestureListener Antworten für diese Frage sind nicht sehr elegant auf Android. Wenn Sie nach dem Code suchen, der beim Wechseln zwischen Fotos in der Android-Galerie oder beim Wechseln von Ansichten in der neuen Play Market-App verwendet wird, dann ist es definitiv ViewPager.

Hier sind einige Links für weitere Informationen:


21
2018-04-05 14:28



Es gibt eine integrierte Schnittstelle, die Sie direkt für alle Gesten verwenden können:
Hier ist eine Erklärung für einen Benutzer auf Basisebene: enter image description here Es gibt 2 Importe, seien Sie vorsichtig bei der Auswahl, dass beide unterschiedlich sind enter image description here enter image description here


14
2018-04-12 21:14



Auch als kleine Verbesserung.

Der Hauptgrund für den try / catch-Block ist, dass e1 für die anfängliche Bewegung null sein kann. Fügen Sie zusätzlich zu try / catch einen Test für null und return ein. ähnlich wie folgt

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

11
2017-11-02 23:14



Hier gibt es viele gute Informationen. Leider ist viel von diesem Fling-Processing-Code auf verschiedenen Seiten in verschiedenen Stadien der Fertigstellung verstreut, obwohl man denken würde, dass dies für viele Anwendungen essentiell ist.

Ich habe mir die Zeit genommen ein zu erstellen schleudern Zuhörer das bestätigt, dass die entsprechenden Bedingungen erfüllt sind. Ich habe eine hinzugefügt Seite fling Listener Dadurch werden weitere Überprüfungen hinzugefügt, um sicherzustellen, dass Flings den Schwellenwert für Seitenumblätter erfüllen. Mit diesen beiden Listenern können Sie Flings einfach auf die horizontale oder vertikale Achse beschränken. Sie können sehen, wie es in einem verwendet wird Ansicht für gleitende Bilder. Ich gebe zu, dass die Leute hier die meiste Recherche gemacht haben - ich habe sie einfach in eine brauchbare Bibliothek zusammengefügt.

Diese letzten Tage sind mein erster Versuch, auf Android zu programmieren. erwarten von viel mehr kommen.


10
2017-12-07 07:49