Fling-Gestenerkennung im Rasterlayout


1106

Ich möchte, dass die flingGestenerkennung in meiner Android-Anwendung funktioniert.

Was ich habe, ist eine GridLayout, die 9 ImageViews enthält . Die Quelle finden Sie hier: Romain Guys 'Grid Layout .

Diese Datei stammt aus der Photostream-Anwendung von Romain Guy und wurde nur geringfügig angepasst.

Für die einfachen Klick Situation muss ich nur setzen die onClickListenerfür jeden ImageViewich hinzufügen die Haupt sein activitywelche Geräte View.OnClickListener. Es scheint unendlich komplizierter, etwas zu implementieren, das a erkennt fling. Ich nehme an, das liegt daran, dass es sich überspannen kann views?

  • Wenn meine Aktivität implementiert wird OnGestureListener, weiß ich nicht, wie ich das als Gestenlistener für die Gridoder die von Imagemir hinzugefügten Ansichten festlegen soll .

    public class SelectFilterActivity extends Activity implements
       View.OnClickListener, OnGestureListener { ...
    
  • Wenn meine Aktivität implementiert OnTouchListenerwird, habe ich keine onFlingMethode dafür override(es gibt zwei Ereignisse als Parameter, mit denen ich feststellen kann, ob der Fling bemerkenswert war).

    public class SelectFilterActivity extends Activity implements
        View.OnClickListener, OnTouchListener { ...
    
  • Wenn ich eine benutzerdefinierte machen View, wie GestureImageViewdass sich ImageViewich weiß nicht , wie die Aktivität zu sagen , dass ein flingaus der Sicht aufgetreten ist. Auf jeden Fall habe ich dies versucht und die Methoden wurden nicht aufgerufen, als ich den Bildschirm berührte.

Ich brauche wirklich nur ein konkretes Beispiel dafür, wie man über Ansichten hinweg arbeitet. Was, wann und wie soll ich das anhängen 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 Ansicht über den oberen Bildschirmrand zu legen, um Schleudern zu erfassen?

Kann ich inflateden GestureDetectorals Konstruktor-Parameter an eine neue Unterklasse übergeben ImageView, die ich erstellt habe, wenn ich meine untergeordneten Bildansichten nicht aus XML auswähle ?

Dies ist die sehr einfache Aktivität, für die ich versuche, die flingErkennung zum Laufen zu bringen : SelectFilterActivity (Angepasst aus Fotostream) .

Ich habe mir diese Quellen angesehen:

Bisher hat nichts für mich funktioniert und ich hatte auf einige Hinweise gehofft.


Wie kann man dieses Problem lösen? Bitte antworten Sie auf stackoverflow.com/questions/60464912/…
Bishwash

Antworten:


818

Vielen Dank an Code Shogun , dessen Code ich an meine Situation angepasst habe.

Lassen Sie Ihre Aktivität OnClickListenerwie gewohnt umsetzen :

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

Fügen Sie Ihren Gesten-Listener allen Ansichten hinzu, die Sie dem Hauptlayout hinzufügen.

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

Beobachten Sie mit Ehrfurcht, wie Ihre überschriebenen Methoden getroffen werden, sowohl die onClick(View v)der Aktivität als auch die onFlingdes Gestenhörers.

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

Der Post-Fling-Tanz ist optional, wird aber empfohlen.


109
Vielen Dank für diesen Code! Es war sehr hilfreich. Ich stieß jedoch auf einen sehr, sehr frustrierenden Haken, als ich versuchte, Gesten zum Laufen zu bringen. In meinem SimpleOnGestureListener muss ich onDown überschreiben, damit sich eine meiner Gesten registriert. Es kann nur true zurückgeben, aber ich muss definiert werden. PS: Ich weiß nicht, ob es meine API-Revision oder meine Hardware ist, aber ich verwende 1.5 auf einem HTC Droid Eris.
Cdsboy

Ich habe Ihren Code ausprobiert und es spielt keine Rolle, ob ich wische oder klicke (mit der Maus, weil ich im Emulator arbeite). Ich erhalte immer einen Toast, den ich in der onClick-Methode definiert habe, sodass der Emulator nur Klicks ohne Wischen erkennt. Wieso ist es so?
Lomza

Ich habe diesen Code ausprobiert und er hat nicht funktioniert. Ich konnte immer noch nicht scrollen, als ich einen onClick-Listener auf eine der untergeordneten Ansichten in meiner Galerieansicht anwendete
Jonathan

Iomza: Haben Sie versucht, break-Anweisungen zu setzen und Ihren Code durchzugehen?
Igor Ganapolsky

Ein großes Lob für die Verwendung einer inneren Klasse! Sehr sauberer Ansatz.
Igor Ganapolsky

211

Eine der obigen Antworten erwähnt die Behandlung unterschiedlicher Pixeldichten, schlägt jedoch vor, die Swipe-Parameter von Hand zu berechnen. Es ist erwähnenswert, dass Sie mithilfe der ViewConfigurationKlasse tatsächlich skalierte, vernünftige Werte vom System erhalten können :

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)

Ich habe festgestellt, dass die Verwendung dieser Werte dazu führt, dass das "Gefühl" des Schleuderns zwischen der Anwendung und dem Rest des Systems konsistenter ist.


11
Ich benutze swipeMinDistance = vc.getScaledPagingTouchSlop()und swipeMaxOffPath = vc.getScaledTouchSlop().
Thomas Ahle

8
getScaledTouchSlopgibt mir sehr wenig Offset-Ergebnis, umständlich. Zum Beispiel nur 24 Pixel auf einem 540 hohen Bildschirm, das ist sehr schwer, es mit dem Finger in Reichweite zu halten. : S
WonderCsabo

148

Ich mache es ein bisschen anders und habe eine zusätzliche Detektorklasse geschrieben, die das implementiert View.onTouchListener

onCreateFügen Sie es einfach wie folgt zum niedrigsten 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 mit dem niedrigsten Wert in der Layouthierarchie und lowLayout als RelativeLayout deklariert

Und dann gibt es noch die eigentliche Aktivitäts-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!


1
Dies machte es mir tatsächlich viel einfacher, Gestenfunktionen anzuwenden, und erforderte "weniger" Verkabelung: D Danke @Thomas
nemesisfixx

5
Dies sieht aus wie eine ordentliche Utility-Klasse - aber ich denke, Ihre vier on ... swipe () -Methoden sollten Schnittstellen sein
Jemand irgendwo

2
Diese Rückgaben sollten nicht vorhanden sein (Zeile "Wir konsumieren das Ereignis nicht"), nicht wahr? Es deaktiviert die vertikale Bildlauffunktion.
Marek Sebera

5
speziell die onTouch () -Methode. Erstens, wenn das Delta X nicht groß genug ist, kehrt es zurück, ohne das Delta Y zu überprüfen. Das Ergebnis ist, dass die Wischbewegungen von links nach rechts niemals erkannt werden. Zweitens sollte es auch nicht true zurückgeben, wenn es durch das Finden eines Wischs nicht mehr funktioniert. Drittens sollte es nicht wahr sein, wenn die Aktion ausgeführt wird. Dies verhindert, dass andere Listener wie onClick funktionieren.
Jeffrey Blattman

1
@Piotr ist kein Problem, solange das Objekt, das die Referenz enthält, den gleichen Umfang hat wie die Aktivität selbst. Das Problem tritt auf, wenn Sie einen Verweis auf eine Aktivität an einem Ort aufbewahren, der einen größeren Umfang als die Aktivität hat, z. B. von einem statischen Element.
Jeffrey Blattman

94

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

Das gesamte 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 verwendet:

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

Bei der Implementierung müssen ActivitySie Methoden von SwipeInterface implementieren , und Sie können herausfinden, in welcher Ansicht das Swipe-Ereignis aufgerufen wurde.

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

Ich habe es wieder leicht modifiziert, siehe das v.performClick();, das verwendet wird, um Ereignis nicht auf OnClickListener zu verbrauchen, wenn auf dieselbe Ansicht gesetzt
Marek Sebera

Hallo, ich bin ein absoluter Anfänger, daher ist diese Frage möglicherweise sehr offensichtlich oder trivial, aber bitte beantworten Sie sie. Der Teil, in den Sie geschrieben haben, wird verwendet als: ActivitySwipeDetector swipe = new ActivitySwipeDetector (this); Diese Aussage wird Teil von MainActivity sein, richtig? Dann ist "dies" eine Aktivität von MainActivity. Während der Konstruktor eine Instanz von SwipeInterface verwendet. Bitte helfen Sie mir hier raus. Vielen Dank.
Chocolava

@Chocolava neue Frage erstellen, Kommentar ist kein guter Ort, um so zu fragen.
Marek Sebera

@MarekSebera funktioniert das nicht mit ScrollView & ListView? Wie gehe ich damit um?
Duc Tran

@silentbang wieder, dies ist kein Ort, um solche Fragen zu stellen. Bitte erstellen Sie einen neuen Fragenthread.
Marek Sebera

65

Der obige Code für den Wischgestendetektor ist sehr nützlich! Möglicherweise möchten Sie diese Lösungsdichte jedoch unabhängig machen, indem Sie die folgenden relativen Werte (REL_SWIPE)anstelle der absoluten Werte verwenden(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);

8
+1 für das Aufrufen. Beachten Sie, dass DensityMetrics.densityDpi in API 4 eingeführt wurde. Verwenden Sie stattdessen DensityMetrics.density, um die Abwärtskompatibilität mit API 1 zu gewährleisten. Dies ändert dann die Berechnung so, dass sie nur SWIPE_MIN_DISTANCE * dm.density ist.
Thane Anthem

Woher hast du die Nummer 160.0f?
IgorGanapolsky

developer.android.com/guide/practices/screens_support.html Dichteunabhängiges Pixel (dp) Die Konvertierung von dp-Einheiten in Bildschirmpixel ist einfach: px = dp * (dpi / 160)
paiego

Ich habe überall danach gesucht. Kein Beispiel für onFling () im Internet hat dies, was zu einer schlechten UX führen wird. Vielen Dank!
Sandy

160.0f kommt von 160 DPI, der Standarddichte, auf der DP (dichteunabhängige Pixel) basiert. public static final int DENSITY_MEDIUM Hinzugefügt in API Level 4 Standard quantisierte DPI für Bildschirme mittlerer Dichte. Konstanter Wert: 160 (0x000000a0)
paiego

35

Meine von Thomas Fankhauser und Marek Sebera vorgeschlagene Version der Lösung (behandelt keine vertikalen Wischbewegungen):

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

}

Kann mir bitte jemand sagen, wie ich die Klasse anrufen soll? ActivitySwipeDetector swipe = neuer ActivitySwipeDetector (this); Offensichtlich gibt es Fehler, da kein solcher Konstruktor. Sollte ich ActivitySwipeDetector geben swipe = new ActivitySwipeDetector (this, null);
Abdfahim

@AbdullahFahim ActivitySwipeDetector (this, YourActivity.this);
Anton Kashpor

25

Diese Frage ist ziemlich alt und im Juli 2011 veröffentlichte Google das Kompatibilitätspaket, Revision 3), das das enthält, das ViewPagermit Android 1.6 oder höher funktioniert. Die GestureListenerAntworten auf diese Frage fühlen sich auf Android nicht sehr elegant an. Wenn Sie nach dem Code suchen, der beim Wechseln zwischen Fotos in der Android-Galerie oder beim Wechseln der Ansicht in der neuen Play Market-App verwendet wird, ist dies definitiv der Fall ViewPager.

Hier sind einige Links für weitere Informationen:


Ein Problem mit ViewPager besteht darin, dass Sie die Entfernungs- und Geschwindigkeitsparameter für die Schleudergeste nicht steuern können.
Almalkawi

ViewPager wird in der Galerie nicht verwendet.
Anthony

18

Es gibt eine integrierte Oberfläche, die Sie direkt für alle Gesten verwenden können:
Hier ist eine Erklärung für einen Benutzer der Grundstufe: Geben Sie hier die Bildbeschreibung ein Es gibt 2 Importe. Achten Sie darauf, dass beide unterschiedlich sind Geben Sie hier die Bildbeschreibung ein Geben Sie hier die Bildbeschreibung ein


1
Und was sind die nächsten Schritte? Wie kann ich diesen Listener auf eine bestimmte Ansicht einstellen? Und was ist, wenn diese Ansicht Teil eines Fragments ist?
Stan

16

Über das Web (und diese Seite) gibt es einige Vorschläge zur Verwendung von ViewConfiguration. getScaledTouchSlop () mit einem geräteskalierten Wert für SWIPE_MIN_DISTANCE.

getScaledTouchSlop()ist für den Abstand " Bildlaufschwelle " vorgesehen, nicht für Wischen. Der Bildlaufschwellenabstand muss kleiner sein als ein Schwellenwertabstand zwischen Seiten. Diese Funktion gibt beispielsweise 12 Pixel auf meinem Samsung GS2 zurück, und die auf dieser Seite aufgeführten Beispiele sind ungefähr 100 Pixel.

Mit API Level 8 (Android 2.2, Froyo) haben Sie es getScaledPagingTouchSlop()für das Wischen von Seiten vorgesehen. Auf meinem Gerät werden 24 (Pixel) zurückgegeben. Wenn Sie sich also auf API-Stufe <8 befinden, sollte "2 * getScaledTouchSlop()" der "Standard" -Schwenkschwellenwert sein. Benutzer meiner Anwendung mit kleinen Bildschirmen sagten mir jedoch, dass es zu wenige waren ... Wie bei meiner Anwendung können Sie vertikal scrollen und die Seite horizontal wechseln. Mit dem vorgeschlagenen Wert wechseln sie manchmal die Seite, anstatt zu scrollen.


13

Auch als kleine Verbesserung.

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

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

12

Hier gibt es viele hervorragende Informationen. Leider ist ein Großteil dieses Fling-Verarbeitungscodes an verschiedenen Standorten in verschiedenen Fertigstellungszuständen verteilt, obwohl man denken würde, dass dies für viele Anwendungen wesentlich ist.

Ich habe mir die Zeit genommen, einen Fling-Listener zu erstellen , der überprüft, ob die entsprechenden Bedingungen erfüllt sind. Ich habe einen Listener für Seitenumbrüche hinzugefügt , der weitere Überprüfungen hinzufügt, um sicherzustellen, dass Flings den Schwellenwert für Seitenumbrüche erfüllen. Mit beiden Listenern können Sie die Flings leicht auf die horizontale oder vertikale Achse beschränken. Sie können sehen, wie es in einer Ansicht zum Verschieben von Bildern verwendet wird . Ich gebe zu, dass die Leute hier den größten Teil der Forschung durchgeführt haben - ich habe es gerade in einer nutzbaren Bibliothek zusammengestellt.

Diese letzten Tage sind mein erster Versuch, auf Android zu programmieren. Erwarten Sie viel mehr .


Ich möchte eine Wischgeste über 2 Finger implementieren. Bitte hilf mir!
Gaurav Arora

12

Dies ist eine kombinierte Antwort der beiden Antworten oben, wenn jemand eine funktionierende Implementierung wünscht.

package com.yourapplication;

import android.content.Context;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;

public abstract class OnSwipeListener implements View.OnTouchListener {

    private final GestureDetector gestureDetector;

    public OnSwipeListener(Context context){
        gestureDetector = new GestureDetector(context, new OnSwipeGestureListener(context));
        gestureDetector.setIsLongpressEnabled(false);
    }

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

    private final class OnSwipeGestureListener extends GestureDetector.SimpleOnGestureListener {

        private final int minSwipeDelta;
        private final int minSwipeVelocity;
        private final int maxSwipeVelocity;

        private OnSwipeGestureListener(Context context) {
            ViewConfiguration configuration = ViewConfiguration.get(context);
            // We think a swipe scrolls a full page.
            //minSwipeDelta = configuration.getScaledTouchSlop();
            minSwipeDelta = configuration.getScaledPagingTouchSlop();
            minSwipeVelocity = configuration.getScaledMinimumFlingVelocity();
            maxSwipeVelocity = configuration.getScaledMaximumFlingVelocity();
        }

        @Override
        public boolean onDown(MotionEvent event) {
            // Return true because we want system to report subsequent events to us.
            return true;
        }

        // NOTE: see http://stackoverflow.com/questions/937313/android-basic-gesture-detection
        @Override
        public boolean onFling(MotionEvent event1, MotionEvent event2, float velocityX,
                               float velocityY) {

            boolean result = false;
            try {
                float deltaX = event2.getX() - event1.getX();
                float deltaY = event2.getY() - event1.getY();
                float absVelocityX = Math.abs(velocityX);
                float absVelocityY = Math.abs(velocityY);
                float absDeltaX = Math.abs(deltaX);
                float absDeltaY = Math.abs(deltaY);
                if (absDeltaX > absDeltaY) {
                    if (absDeltaX > minSwipeDelta && absVelocityX > minSwipeVelocity
                            && absVelocityX < maxSwipeVelocity) {
                        if (deltaX < 0) {
                            onSwipeLeft();
                        } else {
                            onSwipeRight();
                        }
                    }
                    result = true;
                } else if (absDeltaY > minSwipeDelta && absVelocityY > minSwipeVelocity
                        && absVelocityY < maxSwipeVelocity) {
                    if (deltaY < 0) {
                        onSwipeTop();
                    } else {
                        onSwipeBottom();
                    }
                }
                result = true;
            } catch (Exception e) {
                e.printStackTrace();
            }
            return result;
        }
    }

    public void onSwipeLeft() {}

    public void onSwipeRight() {}

    public void onSwipeTop() {}

    public void onSwipeBottom() {}
}

Vielen Dank für eine wirklich gute Umsetzung. Zusätzlich würde ich vorschlagen , nur für den Fall zu überprüfen absDeltaY > minSwipeDelta, ob ! = , ! = , ! = , Dh nur zu überprüfen, ob diese sogenannten "Standard" -Werte (ich meine getScaledTouchSlop, getScaledMinimumFlingVelocity, getScaledMaximumFlingVelocity) entsprechend Ihren Werten skaliert oder geändert werden eigener Wunsch. absVelocityY > minSwipeVelocityabsVelocityY < maxSwipeVelocityminSwipeDelta getScaledTouchSlopminSwipeVelocity getScaledMinimumFlingVelocitymaxSwipeVelocity getScaledMaximumFlingVelocity
Elia12345

Der Punkt ist, dass gemäß dem Quellcode die genannten "Standard" -Werte bereits von GestureDetector überprüft werden und OnFling nur ausgelöst wird, wenn sie bestätigt werden (übrigens erfolgt die Auslösung nur im Fall von ACTION_UP, nicht ACTION_MOVEoder ACTION_POINTER_UP, dh nur als ein Ergebnis der vollständig realisierten Geste). (Ich habe andere API-Versionen nicht überprüft, daher sind Kommentare willkommen.)
Elia12345

11

Sie können die droidQuery- Bibliothek verwenden, um Schleudern, Klicks, lange Klicks und benutzerdefinierte Ereignisse zu verarbeiten. Die Implementierung basiert auf meiner vorherigen Antwort unten, aber droidQuery bietet eine , einfache Syntax:

//global variables    private boolean isSwiping = false;
private SwipeDetector.Direction swipeDirection = null;
private View v;//must be instantiated before next call.

//swipe-handling code
$.with(v).swipe(new Function() {
    @Override
    public void invoke($ droidQuery, Object... params) {
        if (params[0] == SwipeDetector.Direction.START)
            isSwiping = true;
        else if (params[0] == SwipeDetector.Direction.STOP) {
            if (isSwiping) {                    isSwiping = false;
                if (swipeDirection != null) {
                    switch(swipeDirection) {
                        case DOWN :                                //TODO: Down swipe complete, so do something
                            break;
                        case UP :
                            //TODO: Up swipe complete, so do something
                            break;
                        case LEFT :
                            //TODO: Left swipe complete, so do something
                            break;
                        case RIGHT :
                            //TODO: Right swipe complete, so do something
                            break;
                        default :                                break;
                    }
                }                }
        }
        else {
            swipeDirection = (SwipeDetector.Direction) params[0];
        }
    }
});

Ursprüngliche Antwort

Diese Antwort verwendet eine Kombination von Komponenten aus den anderen Antworten hier. Es besteht aus der SwipeDetectorKlasse, die eine innere Schnittstelle zum Abhören von Ereignissen hat. Ich biete auch eine RelativeLayoutan, um zu zeigen, wie man a View's überschreibtonTouch Verfahren beide Swipe Ereignisse und andere erkannten Ereignisse (wie Klicks oder lange Klicks) zu ermöglichen.

SwipeDetector

package self.philbrown;

import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;

/**
 * Detect Swipes on a per-view basis. Based on original code by Thomas Fankhauser on StackOverflow.com,
 * with adaptations by other authors (see link).
 * @author Phil Brown
 * @see <a href="http://stackoverflow.com/questions/937313/android-basic-gesture-detection">android-basic-gesture-detection</a>
 */
public class SwipeDetector implements View.OnTouchListener
{
    /**
     * The minimum distance a finger must travel in order to register a swipe event.
     */
    private int minSwipeDistance;

    /** Maintains a reference to the first detected down touch event. */
    private float downX, downY;

    /** Maintains a reference to the first detected up touch event. */
    private float upX, upY;

    /** provides access to size and dimension contants */
    private ViewConfiguration config;

    /**
     * provides callbacks to a listener class for various swipe gestures.
     */
    private SwipeListener listener;

    public SwipeDetector(SwipeListener listener)
    {
        this.listener = listener;
    }


    /**
     * {@inheritDoc}
     */
    public boolean onTouch(View v, MotionEvent event)
    {
        if (config == null)
        {
                config = ViewConfiguration.get(v.getContext());
                minSwipeDistance = config.getScaledTouchSlop();
        }

        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) > minSwipeDistance)
            {
                // left or right
                if (deltaX < 0)
                {
                        if (listener != null)
                        {
                                listener.onRightSwipe(v);
                                return true;
                        }
                }
                if (deltaX > 0)
                {
                        if (listener != null)
                        {
                                listener.onLeftSwipe(v);
                                return true;
                        }
                }
            }

            // swipe vertical?
            if(Math.abs(deltaY) > minSwipeDistance)
            {
                // top or down
                if (deltaY < 0)
                {
                        if (listener != null)
                        {
                                listener.onDownSwipe(v);
                                return true;
                        }
                }
                if (deltaY > 0)
                {
                        if (listener != null)
                        {
                                listener.onUpSwipe(v);
                                return true;
                        }
                }
            }
        }
        return false;
    }

    /**
     * Provides callbacks to a registered listener for swipe events in {@link SwipeDetector}
     * @author Phil Brown
     */
    public interface SwipeListener
    {
        /** Callback for registering a new swipe motion from the bottom of the view toward its top. */
        public void onUpSwipe(View v);
        /** Callback for registering a new swipe motion from the left of the view toward its right. */
        public void onRightSwipe(View v);
        /** Callback for registering a new swipe motion from the right of the view toward its left. */
        public void onLeftSwipe(View v);
        /** Callback for registering a new swipe motion from the top of the view toward its bottom. */
        public void onDownSwipe(View v);
    }
}

Swipe Interceptor View

package self.philbrown;

import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.RelativeLayout;

import com.npeinc.module_NPECore.model.SwipeDetector;
import com.npeinc.module_NPECore.model.SwipeDetector.SwipeListener;

/**
 * View subclass used for handling all touches (swipes and others)
 * @author Phil Brown
 */
public class SwipeInterceptorView extends RelativeLayout
{
    private SwipeDetector swiper = null;

    public void setSwipeListener(SwipeListener listener)
    {
        if (swiper == null)
            swiper = new SwipeDetector(listener);
    }

    public SwipeInterceptorView(Context context) {
        super(context);
    }

    public SwipeInterceptorView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public SwipeInterceptorView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    public boolean onTouchEvent(MotionEvent e)
    {
        boolean swipe = false, touch = false;
        if (swiper != null)
            swipe = swiper.onTouch(this, e);
        touch = super.onTouchEvent(e);
        return swipe || touch;
    }
}

1
Ich habe versucht, dies in einer Ansicht zu implementieren, die anklickbare Elemente enthält. Wenn ein Wischen über ein anklickbares Element beginnt (z. B. eine Listenansicht, in der der Listener onItemClick registriert ist), wird onTouchEvent niemals aufgerufen. Daher kann der Benutzer nicht über ein anklickbares Element wischen, was für mich unglücklich ist, und ich versuche immer noch herauszufinden, wie dies umgangen werden kann, da unsere anklickbaren Elemente viel Platz in der Ansicht beanspruchen und wir weiterhin Wischunterstützung wünschen für die gesamte Ansicht. Wenn ein Wischen nicht über einem anklickbaren Element beginnt, funktioniert es einwandfrei.
Lo-Tan

@ Lo-Tan, dies tritt auf, weil Ihr anklickbares Element eine untergeordnete Ansicht ist und sich daher über dem befindet SwipeInterceptorView, sodass der Klick zuerst behandelt wird. Sie können dies beheben, indem Sie Ihren eigenen Klickmechanismus implementieren onTouchListener, indem Sie implementieren , oder als Umgehungslösung können Sie auf lange Klicks anstatt auf Klicks warten (siehe View.setOnLongClickListener).
Phil

Das versuche ich gerade. Oder möglicherweise das Klickereignis abbrechen, wenn sie ein Ziehen starten :) Vielen Dank.
Lo-Tan

Eine Lösung besteht darin, den Wischdetektor an jede Ansicht in Ihrer App anzuhängen. Eine andere Möglichkeit besteht darin, onInterceptTouchEvent in Ihrer SwipeInterceptorView zu implementieren .
Edward Falk

7

Ich weiß, dass es zu spät ist, um zu antworten, aber ich poste immer noch die Swipe-Erkennung für ListView , wie man den Swipe Touch Listener in ListView Item verwendet .

Aktualisierung: Exterminator13 (eine der Antworten auf dieser Seite)

Erstellen Sie eine ActivitySwipeDetector.class

package com.example.wocketapp;

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 = "SwipeDetector";
    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();
                v.getParent().requestDisallowInterceptTouchEvent(false);
                return true;
            }

        case MotionEvent.ACTION_MOVE:
            {
                float y_up = event.getY();
                float deltaY = y_up - downY;
                float absDeltaYMove = Math.abs(deltaY);

                if (absDeltaYMove > 60) 
                {
                    v.getParent().requestDisallowInterceptTouchEvent(false);
                } 
                else
                {
                    v.getParent().requestDisallowInterceptTouchEvent(true);
                }
            }

            break;

            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.e(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) 
                {
                     v.getParent().requestDisallowInterceptTouchEvent(true);
                    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)));
                }

                 v.getParent().requestDisallowInterceptTouchEvent(false);

            }
        }
        return false;
    }
    public interface SwipeInterface 
    {

        public void onLeftToRight(View v);

        public void onRightToLeft(View v);
    }

}

Nennen Sie es aus Ihrer Aktivitätsklasse wie folgt:

yourLayout.setOnTouchListener(new ActivitySwipeDetector(this, your_activity.this));

Und vergessen Sie nicht, SwipeInterface zu implementieren , mit dem Sie zwei @ override-Methoden erhalten:

    @Override
    public void onLeftToRight(View v) 
    {
        Log.e("TAG", "L to R");
    }

    @Override
    public void onRightToLeft(View v) 
    {
        Log.e("TAG", "R to L");
    }

Ich finde, dass a MAX_OFF_PATH = 5 * vc.getScaledPagingTouchSlop()für einen Daumenschlag, der sich natürlich in einem leichten Bogen bewegt, bequemer ist.
Qix

4

Gesten sind diese subtilen Bewegungen, um Interaktionen zwischen dem Touchscreen und dem Benutzer auszulösen. Sie dauert zwischen der ersten Berührung des Bildschirms und dem Zeitpunkt, an dem der letzte Finger die Oberfläche verlässt.

Android bietet uns eine Klasse namens GestureDetector, mit der wir häufige Gesten wie Abwärts- und Aufwärtsklopfen, vertikales und horizontales Wischen (Schleudern), langes und kurzes Drücken, doppeltes Tippen usw. Erkennen können . und Hörer an sie anhängen.

Lassen Sie unsere Activity- Klasse GestureDetector.OnDoubleTapListener (für die Erkennung von Gesten mit doppeltem Tippen) und GestureDetector.OnGestureListener implementieren und implementieren Sie alle abstrakten Methoden. Weitere Informationen. Sie können https://developer.android.com/training/gestures/detector.html besuchen . Höflichkeit

Für den Demo-Test. GestureDetectorDemo


4

Wenn Sie keine separate Klasse erstellen oder Code komplex machen möchten,
möchten Sie einfach eine GestureDetector-Variable in OnTouchListener erstellen und Ihren Code einfacher gestalten

namVyuVar kann ein beliebiger Name der Ansicht sein, für die Sie den Listener festlegen müssen

namVyuVar.setOnTouchListener(new View.OnTouchListener()
{
    @Override
    public boolean onTouch(View view, MotionEvent MsnEvtPsgVal)
    {
        flingActionVar.onTouchEvent(MsnEvtPsgVal);
        return true;
    }

    GestureDetector flingActionVar = new GestureDetector(getApplicationContext(), new GestureDetector.SimpleOnGestureListener()
    {
        private static final int flingActionMinDstVac = 120;
        private static final int flingActionMinSpdVac = 200;

        @Override
        public boolean onFling(MotionEvent fstMsnEvtPsgVal, MotionEvent lstMsnEvtPsgVal, float flingActionXcoSpdPsgVal, float flingActionYcoSpdPsgVal)
        {
            if(fstMsnEvtPsgVal.getX() - lstMsnEvtPsgVal.getX() > flingActionMinDstVac && Math.abs(flingActionXcoSpdPsgVal) > flingActionMinSpdVac)
            {
                // TskTdo :=> On Right to Left fling

                return false;
            }
            else if (lstMsnEvtPsgVal.getX() - fstMsnEvtPsgVal.getX() > flingActionMinDstVac && Math.abs(flingActionXcoSpdPsgVal) > flingActionMinSpdVac)
            {
                // TskTdo :=> On Left to Right fling

                return false;
            }

            if(fstMsnEvtPsgVal.getY() - lstMsnEvtPsgVal.getY() > flingActionMinDstVac && Math.abs(flingActionYcoSpdPsgVal) > flingActionMinSpdVac)
            {
                // TskTdo :=> On Bottom to Top fling

                return false;
            }
            else if (lstMsnEvtPsgVal.getY() - fstMsnEvtPsgVal.getY() > flingActionMinDstVac && Math.abs(flingActionYcoSpdPsgVal) > flingActionMinSpdVac)
            {
                // TskTdo :=> On Top to Bottom fling

                return false;
            }
            return false;
        }
    });
});

3

An alle: Vergessen Sie nicht den Fall MotionEvent.ACTION_CANCEL:

Es werden 30% Swipes ohne ACTION_UP aufgerufen

und es ist in diesem Fall gleich ACTION_UP


2

Ich habe eine allgemeinere Klasse erstellt, Tomas 'Klasse genommen und eine Schnittstelle hinzugefügt, die Ereignisse an Ihre Aktivität oder Ihr Fragment sendet. Der Listener wird im Konstruktor registriert. Stellen Sie daher sicher, dass Sie die Schnittstelle implementieren. Andernfalls wird eine ClassCastException ausgeführt. Die Schnittstelle gibt eines der vier in der Klasse definierten endgültigen int zurück und gibt die Ansicht zurück, für die sie aktiviert wurde.

import android.app.Activity;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

public class SwipeDetector implements View.OnTouchListener{

    static final int MIN_DISTANCE = 100;
    private float downX, downY, upX, upY;
    public final static int RIGHT_TO_LEFT=1;
    public final static int LEFT_TO_RIGHT=2;
    public final static int TOP_TO_BOTTOM=3;
    public final static int BOTTOM_TO_TOP=4;
    private View v;

    private onSwipeEvent swipeEventListener;


    public SwipeDetector(Activity activity,View v){
        try{
            swipeEventListener=(onSwipeEvent)activity;
        }
        catch(ClassCastException e)
        {
            Log.e("ClassCastException",activity.toString()+" must implement SwipeDetector.onSwipeEvent");
        } 
        this.v=v;
    }
    public SwipeDetector(Fragment fragment,View v){
        try{
            swipeEventListener=(onSwipeEvent)fragment;
        }
        catch(ClassCastException e)
        {
            Log.e("ClassCastException",fragment.toString()+" must implement SwipeDetector.onSwipeEvent");
        } 
        this.v=v;
    }


    public void onRightToLeftSwipe(){   
        swipeEventListener.SwipeEventDetected(v,RIGHT_TO_LEFT);
    }

    public void onLeftToRightSwipe(){   
        swipeEventListener.SwipeEventDetected(v,LEFT_TO_RIGHT);
    }

    public void onTopToBottomSwipe(){   
        swipeEventListener.SwipeEventDetected(v,TOP_TO_BOTTOM);
    }

    public void onBottomToTopSwipe(){
        swipeEventListener.SwipeEventDetected(v,BOTTOM_TO_TOP);
    }

    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;

            //HORIZONTAL SCROLL
            if(Math.abs(deltaX) > Math.abs(deltaY))
            {
                if(Math.abs(deltaX) > MIN_DISTANCE){
                    // left or right
                    if(deltaX < 0) 
                    {
                        this.onLeftToRightSwipe();
                        return true;
                    }
                    if(deltaX > 0) {
                        this.onRightToLeftSwipe();
                        return true; 
                    }
                }
                else {
                    //not long enough swipe...
                    return false; 
                }
            }
            //VERTICAL SCROLL
            else 
            {
                if(Math.abs(deltaY) > MIN_DISTANCE){
                    // top or down
                    if(deltaY < 0) 
                    { this.onTopToBottomSwipe();
                    return true; 
                    }
                    if(deltaY > 0)
                    { this.onBottomToTopSwipe(); 
                    return true;
                    }
                }
                else {
                    //not long enough swipe...
                    return false;
                }
            }

            return true;
        }
        }
        return false;
    }
    public interface onSwipeEvent
    {
        public void SwipeEventDetected(View v , int SwipeType);
    }

}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.