Android: come gestire i gesti di scorrimento da destra a sinistra


442

Voglio che la mia app riconosca quando un utente passa da destra a sinistra sullo schermo del telefono.

Come fare questo?



Vedi la mia risposta su come scorrere verso l'alto / il basso / a sinistra / a destra gesti stackoverflow.com/questions/13095494/…
fernandohur

Controlla la mia biblioteca che potrebbe essere utile github.com/UdiOshi85/libSwipes
Udi Oshi

1
Controllare risposta accettata in Kotlin qui: stackoverflow.com/a/53791260/2201814
MHSFisher

Risposte:


840

OnSwipeTouchListener.java :

import android.content.Context;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;

public class OnSwipeTouchListener implements OnTouchListener {

    private final GestureDetector gestureDetector;

    public OnSwipeTouchListener (Context ctx){
        gestureDetector = new GestureDetector(ctx, new GestureListener());
    }

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

    private final class GestureListener extends SimpleOnGestureListener {

        private static final int SWIPE_THRESHOLD = 100;
        private static final int SWIPE_VELOCITY_THRESHOLD = 100;

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

        @Override
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
            boolean result = false;
            try {
                float diffY = e2.getY() - e1.getY();
                float diffX = e2.getX() - e1.getX();
                if (Math.abs(diffX) > Math.abs(diffY)) {
                    if (Math.abs(diffX) > SWIPE_THRESHOLD && Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) {
                        if (diffX > 0) {
                            onSwipeRight();
                        } else {
                            onSwipeLeft();
                        }
                        result = true;
                    }
                }
                else if (Math.abs(diffY) > SWIPE_THRESHOLD && Math.abs(velocityY) > SWIPE_VELOCITY_THRESHOLD) {
                    if (diffY > 0) {
                        onSwipeBottom();
                    } else {
                        onSwipeTop();
                    }
                    result = true;
                }
            } catch (Exception exception) {
                exception.printStackTrace();
            }
            return result;
        }
    }

    public void onSwipeRight() {
    }

    public void onSwipeLeft() {
    }

    public void onSwipeTop() {
    }

    public void onSwipeBottom() {
    }
}

Uso:

imageView.setOnTouchListener(new OnSwipeTouchListener(MyActivity.this) {
    public void onSwipeTop() {
        Toast.makeText(MyActivity.this, "top", Toast.LENGTH_SHORT).show();
    }
    public void onSwipeRight() {
        Toast.makeText(MyActivity.this, "right", Toast.LENGTH_SHORT).show();
    }
    public void onSwipeLeft() {
        Toast.makeText(MyActivity.this, "left", Toast.LENGTH_SHORT).show();
    }
    public void onSwipeBottom() {
        Toast.makeText(MyActivity.this, "bottom", Toast.LENGTH_SHORT).show();
    }

});

6
funziona alla grande, ma super.onTouch (view, motionEvent); mi dà un avviso di errore in eclipse "non è definito per l'oggetto tipo". L'eliminazione funziona alla grande.
Opiatefuchs,

22
Grazie funziona come un incantesimo ma dovresti aggiungere un costruttore a OnSwipeTouchListenerquello che riceve un contesto perché quel costruttore GestureDetectorè deprecato dal livello API 3 e crea un'istanza GestureDetectorin quel costruttore.
Hugo Alves,

9
grazie ha funzionato per me con queste modifiche: stackoverflow.com/a/19506010/401403
Arash

3
ma "onDown" non viene mai chiamato. di conseguenza, la mia e1 è sempre nulla e non posso farci nulla.
Mangusta,

3
La mia correzione per questa risposta è passare onTouchalla OnSwipeTouchListenerdefinizione, altrimenti il ​​mio IDE mostrerà l'errore "accesso al membro privato"
Ge Rong,

199

Questo codice rileva gli swipe destro e sinistro, evita le chiamate API deprecate e presenta altri miglioramenti vari rispetto alle risposte precedenti.

/**
 * Detects left and right swipes across a view.
 */
public class OnSwipeTouchListener implements OnTouchListener {

    private final GestureDetector gestureDetector;

    public OnSwipeTouchListener(Context context) {
        gestureDetector = new GestureDetector(context, new GestureListener());
    }

    public void onSwipeLeft() {
    }

    public void onSwipeRight() {
    }

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

    private final class GestureListener extends SimpleOnGestureListener {

        private static final int SWIPE_DISTANCE_THRESHOLD = 100;
        private static final int SWIPE_VELOCITY_THRESHOLD = 100;

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

        @Override
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
            float distanceX = e2.getX() - e1.getX();
            float distanceY = e2.getY() - e1.getY();
            if (Math.abs(distanceX) > Math.abs(distanceY) && Math.abs(distanceX) > SWIPE_DISTANCE_THRESHOLD && Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) {
                if (distanceX > 0)
                    onSwipeRight();
                else
                    onSwipeLeft();
                return true;
            }
            return false;
        }
    }
}

Usalo in questo modo:

view.setOnTouchListener(new OnSwipeTouchListener(context) {
    @Override
    public void onSwipeLeft() {
        // Whatever
    }
});

12
@Jona Avrai sicuramente bisogno di ottenere un buon libro o altra risorsa sui fondamenti di Android; in caso contrario, provare a mettere insieme soluzioni da StackOverflow si rivelerà frustrante. Una buona opzione è la formazione ufficiale per sviluppatori Android . La sezione Avvio di un'attività spiega dove verrai inserito setOnTouchListener(in genere onCreate). contextè il thispuntatore (a meno che non si stia creando un frammento).
Edward Brey,

3
@Lara, non l'ho provato, ma potresti provare a ignorare SimpleOnGestureListener.onSingleTapConfirmed .
Edward Brey,

4
Grazie Edward. Ho anche scoperto che cambiare il ritorno di onDown da vero a falso fa il trucco.
Lara,

2
@Signo, l'attività che contiene la vista fornisce un contesto. Nel caso comune di aggiungere la vista a un frammento, utilizzare Fragment.getActivity().
Edward Brey,

2
@coderVishal A seconda del tuo scopo, potresti trovare utili SwipeRefreshLayout , l' esempio SwipeRefreshLayoutBasic o una delle sue varianti.
Edward Brey,

49

Se è necessario elaborare anche gli eventi dei clic, qui alcune modifiche:

public class OnSwipeTouchListener implements OnTouchListener {

    private final GestureDetector gestureDetector = new GestureDetector(new GestureListener());

    public boolean onTouch(final View v, final MotionEvent event) {
        return gestureDetector.onTouchEvent(event);
    }

    private final class GestureListener extends SimpleOnGestureListener {

        private static final int SWIPE_THRESHOLD = 100;
        private static final int SWIPE_VELOCITY_THRESHOLD = 100;


        @Override
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
            boolean result = false;
            try {
                float diffY = e2.getY() - e1.getY();
                float diffX = e2.getX() - e1.getX();
                if (Math.abs(diffX) > Math.abs(diffY)) {
                    if (Math.abs(diffX) > SWIPE_THRESHOLD && Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) {
                        if (diffX > 0) {
                            result = onSwipeRight();
                        } else {
                            result = onSwipeLeft();
                        }
                    }
                } else {
                    if (Math.abs(diffY) > SWIPE_THRESHOLD && Math.abs(velocityY) > SWIPE_VELOCITY_THRESHOLD) {
                        if (diffY > 0) {
                            result = onSwipeBottom();
                        } else {
                            result = onSwipeTop();
                        }
                    }
                }
            } catch (Exception exception) {
                exception.printStackTrace();
            }
            return result;
        }
    }

    public boolean onSwipeRight() {
        return false;
    }

    public boolean onSwipeLeft() {
        return false;
    }

    public boolean onSwipeTop() {
        return false;
    }

    public boolean onSwipeBottom() {
        return false;
    }
}

E utilizzo del campione:

    background.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View arg0) {
            toggleSomething();
        }
    });
    background.setOnTouchListener(new OnSwipeTouchListener() {
        public boolean onSwipeTop() {
            Toast.makeText(MainActivity.this, "top", Toast.LENGTH_SHORT).show();
            return true;
        }
        public boolean onSwipeRight() {
            Toast.makeText(MainActivity.this, "right", Toast.LENGTH_SHORT).show();
            return true;
        }
        public boolean onSwipeLeft() {
            Toast.makeText(MainActivity.this, "left", Toast.LENGTH_SHORT).show();
            return true;
        }
        public boolean onSwipeBottom() {
            Toast.makeText(MainActivity.this, "bottom", Toast.LENGTH_SHORT).show();
            return true;
        }
    });

5
Non vedo facilmente le modifiche nel tuo OnSwipeTouchListener modificato. Dov'è esattamente?
Nicolas Zozol,

5
Questa dovrebbe essere la risposta accettata ... Le differenze sono sottili ma molto importanti. Innanzitutto, non esiste onDown (). In secondo luogo, i gestori restituiscono un valore booleano per segnalare se hanno consumato l'evento o meno. Ciò è della massima importanza se è necessario più di un solo gestore per la stessa vista (che dovrebbe comunque essere il caso predefinito).
Gábor,

Cos'è quel metodo toggleSomething ()?
tung

1
@tung solo gestore di tocchi normali (non gestuali)
ruX

@NicolasZozol stile codice java, tutte le direzioni di scorrimento, gestore di clic
ruX

28

Espandendo la risposta di Mirek, per il caso in cui si desidera utilizzare i gesti di scorrimento all'interno di una vista di scorrimento. Per impostazione predefinita, il listener di tocco per la vista di scorrimento viene disabilitato e pertanto l'azione di scorrimento non viene eseguita. Per risolvere questo problema, è necessario sostituire il dispatchTouchEventmetodo di Activitye restituire la versione ereditata di questo metodo dopo aver terminato con il proprio listener.

Per apportare alcune modifiche al codice di Mirek: aggiungo un getter per il file gestureDetectorin OnSwipeTouchListener.

public GestureDetector getGestureDetector(){
    return  gestureDetector;
}

Dichiara l' OnSwipeTouchListenerinterno dell'Attività come un campo di classe.

OnSwipeTouchListener onSwipeTouchListener;

Modificare il codice di utilizzo di conseguenza:

onSwipeTouchListener = new OnSwipeTouchListener(MyActivity.this) {
    public void onSwipeTop() {
        Toast.makeText(MyActivity.this, "top", Toast.LENGTH_SHORT).show();
    }
    public void onSwipeRight() {
        Toast.makeText(MyActivity.this, "right", Toast.LENGTH_SHORT).show();
    }
    public void onSwipeLeft() {
        Toast.makeText(MyActivity.this, "left", Toast.LENGTH_SHORT).show();
    }
    public void onSwipeBottom() {
        Toast.makeText(MyActivity.this, "bottom", Toast.LENGTH_SHORT).show();
    }
});

imageView.setOnTouchListener(onSwipeTouchListener);

E sovrascrivi il dispatchTouchEventmetodo all'interno Activity:

@Override
    public boolean dispatchTouchEvent(MotionEvent ev){
        swipeListener.getGestureDetector().onTouchEvent(ev); 
            return super.dispatchTouchEvent(ev);   
    }

Ora entrambe le azioni di scorrimento e scorrimento dovrebbero funzionare.


un segno negativo qui - se faccio un gesto ovunque sulla mia vista di scorrimento, viene chiamato il touchListener, anche se non è stato applicato alla vista di scorrimento stessa, ma a un pulsante casuale al suo interno
Starwave

23

Al fine di avere Click Listener, DoubleClick Listener, OnLongPress Listener, Swipe Left, Swipe Right, Swipe Up, Swipe Downil singolo Viewè necessario setOnTouchListener. vale a dire,

view.setOnTouchListener(new OnSwipeTouchListener(MainActivity.this) {

            @Override
            public void onClick() {
                super.onClick();
                // your on click here
            }

            @Override
            public void onDoubleClick() {
                super.onDoubleClick();
                // your on onDoubleClick here
            }

            @Override
            public void onLongClick() {
                super.onLongClick();
                // your on onLongClick here
            }

            @Override
            public void onSwipeUp() {
                super.onSwipeUp();
                // your swipe up here
            }

            @Override
            public void onSwipeDown() {
                super.onSwipeDown();
                // your swipe down here.
            }

            @Override
            public void onSwipeLeft() {
                super.onSwipeLeft();
                // your swipe left here.
            }

            @Override
            public void onSwipeRight() {
                super.onSwipeRight();
                // your swipe right here.
            }
        });

}

Per questo è necessaria una OnSwipeTouchListenerclasse che implementa OnTouchListener.

public class OnSwipeTouchListener implements View.OnTouchListener {

private GestureDetector gestureDetector;

public OnSwipeTouchListener(Context c) {
    gestureDetector = new GestureDetector(c, new GestureListener());
}

public boolean onTouch(final View view, final MotionEvent motionEvent) {
    return gestureDetector.onTouchEvent(motionEvent);
}

private final class GestureListener extends GestureDetector.SimpleOnGestureListener {

    private static final int SWIPE_THRESHOLD = 100;
    private static final int SWIPE_VELOCITY_THRESHOLD = 100;

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

    @Override
    public boolean onSingleTapUp(MotionEvent e) {
        onClick();
        return super.onSingleTapUp(e);
    }

    @Override
    public boolean onDoubleTap(MotionEvent e) {
        onDoubleClick();
        return super.onDoubleTap(e);
    }

    @Override
    public void onLongPress(MotionEvent e) {
        onLongClick();
        super.onLongPress(e);
    }

    // Determines the fling velocity and then fires the appropriate swipe event accordingly
    @Override
    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
        boolean result = false;
        try {
            float diffY = e2.getY() - e1.getY();
            float diffX = e2.getX() - e1.getX();
            if (Math.abs(diffX) > Math.abs(diffY)) {
                if (Math.abs(diffX) > SWIPE_THRESHOLD && Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) {
                    if (diffX > 0) {
                        onSwipeRight();
                    } else {
                        onSwipeLeft();
                    }
                }
            } else {
                if (Math.abs(diffY) > SWIPE_THRESHOLD && Math.abs(velocityY) > SWIPE_VELOCITY_THRESHOLD) {
                    if (diffY > 0) {
                        onSwipeDown();
                    } else {
                        onSwipeUp();
                    }
                }
            }
        } catch (Exception exception) {
            exception.printStackTrace();
        }
        return result;
    }
}

public void onSwipeRight() {
}

public void onSwipeLeft() {
}

public void onSwipeUp() {
}

public void onSwipeDown() {
}

public void onClick() {

}

public void onDoubleClick() {

}

public void onLongClick() {

}
}

2
La soluzione di Zala è semplice e chiara, mi ha aiutato molto con le azioni di scorrimento in Android. Questa soluzione risolverà i principianti che hanno problemi con i gesti di scorrimento.
Jennifer,

2
@Jaydipsinh Zala, mi risparmi tempo, fantastico. questo risolverà il mio problema di indicatore di scorrimento superiore e inferiore durante lo scorrimento.
TejaDroid,

1
Questa è quella che chiamo una risposta completa. Quando ho aggiunto gli swipe, ho perso le proprietà del mio clic, quindi ho dovuto sostituire il metodo onClick come in questa risposta. Grazie ragazzo!
Presto Santos, il

@Jaydipsinh Zala Non sono sicuro di cosa sto facendo di sbagliato, quando aggiungo OnSwipeTouchListener al mio webView, rimuove l'interazione con il sito Web all'interno di webview. Potete aiutarmi per favore?
AL̲̳I

11

Non hai bisogno di calcoli complicati. Può essere fatto semplicemente usando l' OnGestureListenerinterfaccia della GestureDetectorclasse.

All'interno del onFlingmetodo è possibile rilevare tutte e quattro le direzioni in questo modo:

MyGestureListener.java:

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

public class MyGestureListener implements GestureDetector.OnGestureListener{

    private static final long VELOCITY_THRESHOLD = 3000;

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

    @Override
    public void onShowPress(final MotionEvent e){ }

    @Override
    public boolean onSingleTapUp(final MotionEvent e){ return false; }

    @Override
    public boolean onScroll(final MotionEvent e1, final MotionEvent e2, final float distanceX,
                        final float distanceY){ return false; }

    @Override
    public void onLongPress(final MotionEvent e){ }

    @Override
    public boolean onFling(final MotionEvent e1, final MotionEvent e2,
                       final float velocityX,
                       final float velocityY){

        if(Math.abs(velocityX) < VELOCITY_THRESHOLD 
                    && Math.abs(velocityY) < VELOCITY_THRESHOLD){
            return false;//if the fling is not fast enough then it's just like drag
        }

        //if velocity in X direction is higher than velocity in Y direction,
        //then the fling is horizontal, else->vertical
        if(Math.abs(velocityX) > Math.abs(velocityY)){
            if(velocityX >= 0){
                Log.i("TAG", "swipe right");
            }else{//if velocityX is negative, then it's towards left
                Log.i("TAG", "swipe left");
            }
        }else{
            if(velocityY >= 0){
                Log.i("TAG", "swipe down");
            }else{
                Log.i("TAG", "swipe up");
            }
        }

        return true;
    }
}

utilizzo:

GestureDetector mDetector = new GestureDetector(MainActivity.this, new MyGestureListener());

view.setOnTouchListener(new View.OnTouchListener(){
    @Override
    public boolean onTouch(final View v, final MotionEvent event){
        return mDetector.onTouchEvent(event);
    }
});

setacciato tutta la spazzatura qui, hai ragione, non c'è bisogno di cose troppo complicate - questa funziona perfettamente
Starwave

VELOCITY_THRESHOLD non dovrebbe dipendere dalla densità dello schermo?
Gerrit Beuze,

@GerritBeuze No, il sistema Android lo gestisce. Invia i giusti valori attraverso velocityXe velocityYnel onFlingmetodo. anche se puoi sperimentare per vedere quale valore si adatta meglio alle tue esigenze, ma il numero finale sarebbe universale.
MDP,

Le risposte qui suppongono il contrario: non sono ridimensionate? : stackoverflow.com/questions/18812479/…
Gerrit Beuze

@GerritBeuze È perché stanno facendo la matematica manualmente calcolando la quantità di pixel che il dito ha percorso e questo è sbagliato perché dipende dalla densità dei pixel. dovresti usare solo la velocità come ho fatto io, la velocità è quasi indipendente dal dpi.
MDP


10

La versione Kotlin di @Mirek Rusin è qui:

OnSwipeTouchListener.kt:

open class OnSwipeTouchListener(ctx: Context) : OnTouchListener {

    private val gestureDetector: GestureDetector

    companion object {

        private val SWIPE_THRESHOLD = 100
        private val SWIPE_VELOCITY_THRESHOLD = 100
    }

    init {
        gestureDetector = GestureDetector(ctx, GestureListener())
    }

    override fun onTouch(v: View, event: MotionEvent): Boolean {
        return gestureDetector.onTouchEvent(event)
    }

    private inner class GestureListener : SimpleOnGestureListener() {


        override fun onDown(e: MotionEvent): Boolean {
            return true
        }

        override fun onFling(e1: MotionEvent, e2: MotionEvent, velocityX: Float, velocityY: Float): Boolean {
            var result = false
            try {
                val diffY = e2.y - e1.y
                val diffX = e2.x - e1.x
                if (Math.abs(diffX) > Math.abs(diffY)) {
                    if (Math.abs(diffX) > SWIPE_THRESHOLD && Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) {
                        if (diffX > 0) {
                            onSwipeRight()
                        } else {
                            onSwipeLeft()
                        }
                        result = true
                    }
                } else if (Math.abs(diffY) > SWIPE_THRESHOLD && Math.abs(velocityY) > SWIPE_VELOCITY_THRESHOLD) {
                    if (diffY > 0) {
                        onSwipeBottom()
                    } else {
                        onSwipeTop()
                    }
                    result = true
                }
            } catch (exception: Exception) {
                exception.printStackTrace()
            }

            return result
        }


    }

    open fun onSwipeRight() {}

    open fun onSwipeLeft() {}

    open fun onSwipeTop() {}

    open fun onSwipeBottom() {}
}

Uso:

view.setOnTouchListener(object : OnSwipeTouchListener(context) {

    override fun onSwipeTop() {
        super.onSwipeTop()
    }

    override fun onSwipeBottom() {
        super.onSwipeBottom()
    }

    override fun onSwipeLeft() {
        super.onSwipeLeft()
    }

    override fun onSwipeRight() {
        super.onSwipeRight()
    }
})

la openparola chiave era il punto per me ...


9

Per aggiungere un onClick, ecco cosa ho fatto.

....
// in OnSwipeTouchListener class

private final class GestureListener extends SimpleOnGestureListener {

    .... // normal GestureListener  code

   @Override
    public boolean onSingleTapConfirmed(MotionEvent e) {
        onClick(); // my method
        return super.onSingleTapConfirmed(e);
    }

} // end GestureListener class

    public void onSwipeRight() {
    }

    public void onSwipeLeft() {
    }

    public void onSwipeTop() {
    }

    public void onSwipeBottom() {
    }

    public void onClick(){ 
    }


    // as normal
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        return gestureDetector.onTouchEvent(event);
}

} // end OnSwipeTouchListener class

Sto usando Fragments, quindi uso getActivity () per il contesto. È così che l'ho implementato e funziona.


myLayout.setOnTouchListener(new OnSwipeTouchListener(getActivity()) {
            public void onSwipeTop() {
                Toast.makeText(getActivity(), "top", Toast.LENGTH_SHORT).show();
            }
            public void onSwipeRight() {
                Toast.makeText(getActivity(), "right", Toast.LENGTH_SHORT).show();
            }
            public void onSwipeLeft() {
                Toast.makeText(getActivity(), "left", Toast.LENGTH_SHORT).show();
            }
            public void onSwipeBottom() {
                Toast.makeText(getActivity(), "bottom", Toast.LENGTH_SHORT).show();
            }

            public void onClick(){
                Toast.makeText(getActivity(), "clicked", Toast.LENGTH_SHORT).show();
            }
        });

5

Il metodo di @Edward Brey funziona alla grande. Se qualcuno desidera anche copiare e incollare le importazioni per il OnSwipeTouchListener, eccole qui:

 import android.content.Context;
 import android.view.GestureDetector;
 import android.view.GestureDetector.SimpleOnGestureListener;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.View.OnTouchListener;

3

Una piccola modifica della risposta di @Mirek Rusin e ora puoi rilevare i colpi multitouch. Questo codice è su Kotlin:

class OnSwipeTouchListener(ctx: Context, val onGesture: (gestureCode: Int) -> Unit) : OnTouchListener {

private val SWIPE_THRESHOLD = 200
private val SWIPE_VELOCITY_THRESHOLD = 200

private val gestureDetector: GestureDetector

var fingersCount = 0

fun resetFingers() {
    fingersCount = 0
}

init {
    gestureDetector = GestureDetector(ctx, GestureListener())
}

override fun onTouch(v: View, event: MotionEvent): Boolean {
    if (event.pointerCount > fingersCount) {
        fingersCount = event.pointerCount
    }
    return gestureDetector.onTouchEvent(event)
}

private inner class GestureListener : SimpleOnGestureListener() {

    override fun onDown(e: MotionEvent): Boolean {
        return true
    }

    override fun onFling(e1: MotionEvent, e2: MotionEvent, velocityX: Float, velocityY: Float): Boolean {
        var result = false
        try {
            val diffY = e2.y - e1.y
            val diffX = e2.x - e1.x
            if (Math.abs(diffX) > Math.abs(diffY)) {
                if (Math.abs(diffX) > SWIPE_THRESHOLD && Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) {
                    if (diffX > 0) {
                        val gesture = when (fingersCount) {
                            1 -> Gesture.SWIPE_RIGHT
                            2 -> Gesture.TWO_FINGER_SWIPE_RIGHT
                            3 -> Gesture.THREE_FINGER_SWIPE_RIGHT
                            else -> -1
                        }
                        if (gesture > 0) {
                            onGesture.invoke(gesture)
                        }
                    } else {
                        val gesture = when (fingersCount) {
                            1 -> Gesture.SWIPE_LEFT
                            2 -> Gesture.TWO_FINGER_SWIPE_LEFT
                            3 -> Gesture.THREE_FINGER_SWIPE_LEFT
                            else -> -1
                        }
                        if (gesture > 0) {
                            onGesture.invoke(gesture)
                        }
                    }
                    resetFingers()
                }
            } else if (Math.abs(diffY) > SWIPE_THRESHOLD && Math.abs(velocityY) > SWIPE_VELOCITY_THRESHOLD) {
                if (diffY > 0) {
                    val gesture = when (fingersCount) {
                        1 ->  Gesture.SWIPE_DOWN
                        2 -> Gesture.TWO_FINGER_SWIPE_DOWN
                        3 -> Gesture.THREE_FINGER_SWIPE_DOWN
                        else -> -1
                    }
                    if (gesture > 0) {
                        onGesture.invoke(gesture)
                    }
                } else {
                    val gesture = when (fingersCount) {
                        1 ->  Gesture.SWIPE_UP
                        2 -> Gesture.TWO_FINGER_SWIPE_UP
                        3 -> Gesture.THREE_FINGER_SWIPE_UP
                        else -> -1
                    }
                    if (gesture > 0) {
                        onGesture.invoke(gesture)
                    }
                }
                resetFingers()
            }
            result = true

        } catch (exception: Exception) {
            exception.printStackTrace()
        }

        return result
    }
}}

Dove Gesture.SWIPE_RIGHT e altri sono un unico identificatore intero di gesto che sto usando per rilevare il tipo di gesto più avanti nella mia attività:

rootView?.setOnTouchListener(OnSwipeTouchListener(this, {
    gesture -> log(Gesture.parseName(this, gesture))
}))

Quindi vedi che il gesto qui è una variabile intera che contiene valore che ho passato prima.


Qualcuno può mostrarmi un esempio di come usare Kotlin per rilevare sia lo scorrimento sinistro sia un clic normale nella stessa vista.
user3561494

Come lo usi in una vista elenco? Non restituisce la posizione della vista che è stata toccata, quindi come fai a sapere quale riga è stata toccata?
user3561494

Il gesto viene da quale importazione? import android.gesture.Gesture non ha un SWIPE_RIGHT globale.
JPM,

perché non hai scritto il codice completo di questo gesto, dove mettere SwipeDown, left, right etc?
Anonimo-E

3

La mia soluzione è simile a quelle sopra, ma ho astratto la gestione dei gesti in una classe astratta OnGestureRegisterListener.java, che include gesti di scorrimento , clic e clic lungo .

OnGestureRegisterListener.java

public abstract class OnGestureRegisterListener implements View.OnTouchListener {

    private final GestureDetector gestureDetector;
    private View view;

    public OnGestureRegisterListener(Context context) {
        gestureDetector = new GestureDetector(context, new GestureListener());
    }

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

    public abstract void onSwipeRight(View view);
    public abstract void onSwipeLeft(View view);
    public abstract void onSwipeBottom(View view);
    public abstract void onSwipeTop(View view);
    public abstract void onClick(View view);
    public abstract boolean onLongClick(View view);

    private final class GestureListener extends GestureDetector.SimpleOnGestureListener {

        private static final int SWIPE_THRESHOLD = 100;
        private static final int SWIPE_VELOCITY_THRESHOLD = 100;

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

        @Override
        public void onLongPress(MotionEvent e) {
            onLongClick(view);
            super.onLongPress(e);
        }

        @Override
        public boolean onSingleTapUp(MotionEvent e) {
            onClick(view);
            return super.onSingleTapUp(e);
        }

        @Override
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
            boolean result = false;
            try {
                float diffY = e2.getY() - e1.getY();
                float diffX = e2.getX() - e1.getX();
                if (Math.abs(diffX) > Math.abs(diffY)) {
                    if (Math.abs(diffX) > SWIPE_THRESHOLD && Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) {
                        if (diffX > 0) {
                            onSwipeRight(view);
                        } else {
                            onSwipeLeft(view);
                        }
                        result = true;
                    }
                }
                else if (Math.abs(diffY) > SWIPE_THRESHOLD && Math.abs(velocityY) > SWIPE_VELOCITY_THRESHOLD) {
                    if (diffY > 0) {
                        onSwipeBottom(view);
                    } else {
                        onSwipeTop(view);
                    }
                    result = true;
                }
            } catch (Exception exception) {
                exception.printStackTrace();
            }
            return result;
        }

    }
}

E usalo così. Nota che puoi anche passare facilmente il tuo Viewparametro.

OnGestureRegisterListener onGestureRegisterListener = new OnGestureRegisterListener(this) {
    public void onSwipeRight(View view) {
        // Do something
    }
    public void onSwipeLeft(View view) {
        // Do something
    }
    public void onSwipeBottom(View view) {
        // Do something
    }
    public void onSwipeTop(View view) {
        // Do something
    }
    public void onClick(View view) {
        // Do something
    }
    public boolean onLongClick(View view) { 
        // Do something
        return true;
    }
};

Button button = findViewById(R.id.my_button);
button.setOnTouchListener(onGestureRegisterListener);

3

Ho fatto cose simili, ma solo per i colpi orizzontali

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

abstract class OnHorizontalSwipeListener(val context: Context) : View.OnTouchListener {    

    companion object {
         const val SWIPE_MIN = 50
         const val SWIPE_VELOCITY_MIN = 100
    }

    private val detector = GestureDetector(context, GestureListener())

    override fun onTouch(view: View, event: MotionEvent) = detector.onTouchEvent(event)    

    abstract fun onRightSwipe()

    abstract fun onLeftSwipe()

    private inner class GestureListener : GestureDetector.SimpleOnGestureListener() {    

        override fun onDown(e: MotionEvent) = true

        override fun onFling(e1: MotionEvent, e2: MotionEvent, velocityX: Float, velocityY: Float)
            : Boolean {

            val deltaY = e2.y - e1.y
            val deltaX = e2.x - e1.x

            if (Math.abs(deltaX) < Math.abs(deltaY)) return false

            if (Math.abs(deltaX) < SWIPE_MIN
                    && Math.abs(velocityX) < SWIPE_VELOCITY_MIN) return false

            if (deltaX > 0) onRightSwipe() else onLeftSwipe()

            return true
        }
    }
}

E quindi può essere utilizzato per visualizzare i componenti

private fun listenHorizontalSwipe(view: View) {
    view.setOnTouchListener(object : OnHorizontalSwipeListener(context!!) {
            override fun onRightSwipe() {
                Log.d(TAG, "Swipe right")
            }

            override fun onLeftSwipe() {
                Log.d(TAG, "Swipe left")
            }

        }
    )
}

3

Questa domanda è stata posta molti anni fa. Ora esiste una soluzione migliore: SmartSwipe: https://github.com/luckybilly/SmartSwipe

il codice è simile al seguente:

SmartSwipe.wrap(contentView)
        .addConsumer(new StayConsumer()) //contentView stay while swiping with StayConsumer
        .enableAllDirections() //enable directions as needed
        .addListener(new SimpleSwipeListener() {
            @Override
            public void onSwipeOpened(SmartSwipeWrapper wrapper, SwipeConsumer consumer, int direction) {
                //direction: 
                //  1: left
                //  2: right
                //  4: top
                //  8: bottom
            }
        })
;

Ci sono molti SwipeConsumer per diversi effetti di slittamento laterale, come SlidingConsumer / StretchConsumer / SpaceConsumer / ... e così via in SmartSwipe
luckybilly

1

@Mirek Rusin answeir è molto bravo. Ma c'è un piccolo bug e la correzione è richiesta -

public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
            boolean result = false;
            try {
                float diffY = e2.getY() - e1.getY();
                float diffX = e2.getX() - e1.getX();
                if (Math.abs(diffX) > Math.abs(diffY)) {
                    if (Math.abs(diffX) > SWIPE_THRESHOLD && Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) {
                        if (diffX > 0) {
                            if (getOnSwipeListener() != null) {
                                getOnSwipeListener().onSwipeRight();
                            }
                        } else {
                            if (getOnSwipeListener() != null) {
                                getOnSwipeListener().onSwipeLeft();
                            }
                        }
                        result = true;
                    }
                }
                else if (Math.abs(diffY) > SWIPE_THRESHOLD && Math.abs(velocityY) > SWIPE_VELOCITY_THRESHOLD) {
                    if (diffY > 0) {
                        if (getOnSwipeListener() != null) {
                            getOnSwipeListener().onSwipeBottom();
                        }
                    } else {
                        if (getOnSwipeListener() != null) {
                            getOnSwipeListener().onSwipeTop();
                        }
                    }
                    result = true;
                }

Qual è la differenza? Impostiamo result = true, solo se abbiamo verificato che tutti i requisiti (sia SWIPE_THRESHOLD che SWIPE_VELOCITY_THRESHOLD sono ok). Questo è importante se scarichiamo lo swipe se alcuni dei requisiti non vengono raggiunti e dobbiamo fare il metodo onTouchEvent di OnSwipeTouchListener!


1

Ecco un semplice codice Android per rilevare la direzione dei gesti

In MainActivity.javae activity_main.xml, scrivi il seguente codice:

MainActivity.java

import java.util.ArrayList;

import android.app.Activity;
import android.gesture.Gesture;
import android.gesture.GestureLibraries;
import android.gesture.GestureLibrary;
import android.gesture.GestureOverlayView;
import android.gesture.GestureOverlayView.OnGesturePerformedListener;
import android.gesture.GestureStroke;
import android.gesture.Prediction;
import android.os.Bundle;
import android.widget.Toast;

public class MainActivity extends Activity implements
        OnGesturePerformedListener {

    GestureOverlayView gesture;
    GestureLibrary lib;
    ArrayList<Prediction> prediction;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        lib = GestureLibraries.fromRawResource(MainActivity.this,
                R.id.gestureOverlayView1);
        gesture = (GestureOverlayView) findViewById(R.id.gestureOverlayView1);
        gesture.addOnGesturePerformedListener(this);
    }

    @Override
    public void onGesturePerformed(GestureOverlayView overlay, Gesture gesture) {
        ArrayList<GestureStroke> strokeList = gesture.getStrokes();
        // prediction = lib.recognize(gesture);
        float f[] = strokeList.get(0).points;
        String str = "";

        if (f[0] < f[f.length - 2]) {
            str = "Right gesture";
        } else if (f[0] > f[f.length - 2]) {
            str = "Left gesture";
        } else {
            str = "no direction";
        }
        Toast.makeText(getApplicationContext(), str, Toast.LENGTH_LONG).show();

    }

}

activity_main.xml

<android.gesture.GestureOverlayView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:android1="http://schemas.android.com/apk/res/android"
    xmlns:android2="http://schemas.android.com/apk/res/android"
    android:id="@+id/gestureOverlayView1"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android1:orientation="vertical" >

    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Draw gesture"
        android:textAppearance="?android:attr/textAppearanceMedium" />

</android.gesture.GestureOverlayView>

1
import android.content.Context
import android.view.GestureDetector
import android.view.GestureDetector.SimpleOnGestureListener
import android.view.MotionEvent
import android.view.View
import android.view.View.OnTouchListener

/**
 * Detects left and right swipes across a view.
 */
class OnSwipeTouchListener(context: Context, onSwipeCallBack: OnSwipeCallBack?) : OnTouchListener {

    private var gestureDetector : GestureDetector
    private var onSwipeCallBack: OnSwipeCallBack?=null

    init {

        gestureDetector = GestureDetector(context, GestureListener())
        this.onSwipeCallBack = onSwipeCallBack!!
    }
    companion object {

        private val SWIPE_DISTANCE_THRESHOLD = 100
        private val SWIPE_VELOCITY_THRESHOLD = 100
    }

   /* fun onSwipeLeft() {}

    fun onSwipeRight() {}*/

    override fun onTouch(v: View, event: MotionEvent): Boolean {


        return gestureDetector.onTouchEvent(event)
    }

    private inner class GestureListener : SimpleOnGestureListener() {

        override fun onDown(e: MotionEvent): Boolean {
            return true
        }

        override fun onFling(eve1: MotionEvent?, eve2: MotionEvent?, velocityX: Float, velocityY: Float): Boolean {
            try {
                if(eve1 != null&& eve2!= null) {
                    val distanceX = eve2?.x - eve1?.x
                    val distanceY = eve2?.y - eve1?.y
                    if (Math.abs(distanceX) > Math.abs(distanceY) && Math.abs(distanceX) > SWIPE_DISTANCE_THRESHOLD && Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) {
                        if (distanceX > 0)
                            onSwipeCallBack!!.onSwipeLeftCallback()
                        else
                            onSwipeCallBack!!.onSwipeRightCallback()
                        return true
                    }
                }
            }catch (exception:Exception){
                exception.printStackTrace()
            }

            return false
        }


    }
}

Per Kotlin Puoi usare qualcosa del genere:
Kamal

Utilizzo: gv_calendar !!. SetOnTouchListener (OnSwipeTouchListener (attività, onSwipeCallBack !!))
kamal

5
Si prega di non solo scaricare il codice, includere una spiegazione di ciò che fa il codice.
Mark Rotteveel,

che cos'è OnSwipeCallBack?
toshkinl,

0

Se si desidera visualizzare alcuni pulsanti con azioni quando si fa scorrere un elemento dell'elenco, molte librerie su Internet hanno questo comportamento. Ho implementato la biblioteca che ho trovato su Internet e sono molto soddisfatto. È molto semplice da usare e molto veloce. Ho migliorato la libreria originale e ho aggiunto un nuovo listener di clic per il clic dell'elemento. Inoltre ho aggiunto una fantastica libreria di caratteri ( http://fortawesome.github.io/Font-Awesome/ ) e ora puoi semplicemente aggiungere un nuovo titolo per l'elemento e specificare il nome dell'icona da fantastico carattere.

Ecco il link github


0
public class TranslatorSwipeTouch implements OnTouchListener
{
   private String TAG="TranslatorSwipeTouch";

   @SuppressWarnings("deprecation")
   private GestureDetector detector=new GestureDetector(new TranslatorGestureListener());

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

private class TranslatorGestureListener extends SimpleOnGestureListener 
{
    private final int GESTURE_THRESHOULD=100;
    private final int GESTURE_VELOCITY_THRESHOULD=100;

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

    @Override
    public boolean onFling(MotionEvent event1,MotionEvent event2,float velocityx,float velocityy)
    {
        try
        {
            float diffx=event2.getX()-event1.getX();
            float diffy=event2.getY()-event1.getY();

            if(Math.abs(diffx)>Math.abs(diffy))
            {
                if(Math.abs(diffx)>GESTURE_THRESHOULD && Math.abs(velocityx)>GESTURE_VELOCITY_THRESHOULD)
                {
                    if(diffx>0)
                    {
                        onSwipeRight();
                    }
                    else
                    {
                        onSwipeLeft();
                    }
                }
            }
            else
            {
                if(Math.abs(diffy)>GESTURE_THRESHOULD && Math.abs(velocityy)>GESTURE_VELOCITY_THRESHOULD)
                {
                    if(diffy>0)
                    {
                         onSwipeBottom();
                    }
                    else
                    {
                        onSwipeTop();
                    }
                }
            }
        }
        catch(Exception e)
        {
            Log.d(TAG, ""+e.getMessage());
        }
        return false;           
    }

    public void onSwipeRight()
    {
        //Toast.makeText(this.getClass().get, "swipe right", Toast.LENGTH_SHORT).show();
        Log.i(TAG, "Right");
    }
    public void onSwipeLeft()
    {
        Log.i(TAG, "Left");
        //Toast.makeText(MyActivity.this, "swipe left", Toast.LENGTH_SHORT).show();
    }

    public void onSwipeTop()
    {
        Log.i(TAG, "Top");
        //Toast.makeText(MyActivity.this, "swipe top", Toast.LENGTH_SHORT).show();
    }

    public void onSwipeBottom()
    {
        Log.i(TAG, "Bottom");
        //Toast.makeText(MyActivity.this, "swipe bottom", Toast.LENGTH_SHORT).show();
    }   

  }

 }

2
Grandi blocchi di codice hanno davvero bisogno di spiegazioni per dare valore ai lettori. Soprattutto il codice che sopprime gli avvisi di deprecazione. Devi davvero spiegare perché quel codice è assolutamente essenziale in questo caso.
Stefan,

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.