Android: Gesten
Willemers Informatik-Ecke

Mit Android ist es möglich, verschiedene Gesten zu verarbeiten. Als Beispiel werden auf dieser Seite das Wischen über den Bildschirm oder das doppelte Tappen auf den Schirm ausgewählt.

Das Programm fängt die beiden Ereignisse und gibt diese auf fünf TextViews aus, die als tv, velx, vely, ev1 und ev2 in der Layout-XML-Datei definiert sein müssen.

Gesten sind TouchEvents

Gesten sind Sonderfälle der Touch-Ereignisse. Die Berührung einer Activity lässt sich einfach fangen, indem die Activity die Methode onTouchEvent überschreibt. Dazu muss kein Listener implementiert werden.
@Override
public boolean onTouchEvent(MotionEvent event) {
    // ...
    return super.onTouchEvent(event);
}
Durch das Auswerten der Ereignisse könnten die Gesten erkannt werden. Allerdings ist das deutlich mühsamer, als einen GestureDetector einzusetzen. Dieser ist darauf spezialisiert, Gesten zu erkennen. Er übernimmt die TouchEvents und wertet sie aus.

Allerdings ist er nicht der einzige Interessent für die Touch-Ereignisse. Auch eine ScrollView wertet die Touch-Ereignisse aus und kommt dann der Gesten-Erkennung empfindlich in die Quere. Darum kann man nur eines von beiden einsetzen.

Wischen von Hand

Fangen wir ganz friedlich an, indem wir die TouchEvents selbst auswerten. Dazu wird das übergebene event auf ACTION-Konstanten abgefragt, die im Parameter mit übergeben werden.
@Override
public boolean onTouchEvent(MotionEvent event) {
    if (event.getAction()==MotionEvent.ACTION_DOWN) {
Es gibt mehrere Konstanten für die verschiedenen Aktionen. Des weiteren kann dem Event entnommen werden, wo die Aktion geschah, indem die Methoden getX() und getY() aufgerufen werden. Beide liefern einen float-Wert.

Um eine Wischbewegung nach links oder rechts zu entdecken, kann man sich die X-Position bei einem DOWN-Event merken und beim UP-Event prüfen, ob die X-Position links oder rechts davon liegt. Um leichtes Wackeln nicht gleich als Wischbewegung zu interpretieren, kann man eine Differenz vorgeben, im Beispiel sind das 100 Pixel.

package de.willemer.wischtest;

import androidx.appcompat.app.AppCompatActivity;

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

public class MainActivity extends AppCompatActivity {

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

    private int action = -1;
    private float posX;
    private static final int swipeDiff = 100;

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (event.getAction()==MotionEvent.ACTION_DOWN) {
            action = MotionEvent.ACTION_DOWN;
            posX = event.getX();
        } else if (action == MotionEvent.ACTION_DOWN) {
            if (event.getAction()==MotionEvent.ACTION_UP) {
                if (event.getX() - posX > swipeDiff) {
                    Log.d("wisch", "rechts");
                } else if (posX - event.getX() > swipeDiff){
                    Log.d("wisch", "links");
                }
                action = -1;
            }
        }
        return false;
    }
}

GestureDetector, der Gestenspezialist

Spezialist für das Auswerten der Gesten ist der GestureDetector. Es handelt sich um eine Klasse, die um die eigenen Reaktionen auf die Gesten erweitert wird, die die eigene App bearbeiten will.

Der GestureDetector wird in der Methode onTouchEvent eingeschoben. Über ihn wird das Touch-Ereignis weitergeleitet.

    private GestureDetector detector = null;
    // ...
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // ...
        this.detector = new GestureDetector(this, new MeinGestenListener());
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        this.detector.onTouchEvent(event);
        return super.onTouchEvent(event);
    }

GestureListener

Die eigentlichen Gestenereignisse werden in der Klasse MeinGestenListener abgearbeitet, die dazu SimpleOnGestureListener erweitert.

Dazu muss die Methode onDraw implementiert werden. Weitere Methoden können überschrieben werden, um bestimmte Gesten zu empfangen.

Alles zusammen

Das folgende Beispiel ist eine App mit einer Activity, auf deren Bildschirm sich fünf TextViews tummeln. Diese müssen in der activity_main.xml angelegt werden. In diesen werden die Ereignisse angezeigt.
package de.willemer.gesten;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.util.Log;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "Gesten";
    private GestureDetector detector = null;
    private TextView tv1, tv2, tvx, tvy, tv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tv = (TextView) findViewById(R.id.tv);
        tvx = (TextView) findViewById(R.id.velx);
        tvy = (TextView) findViewById(R.id.vely);
        tv1 = (TextView) findViewById(R.id.ev1);
        tv2 = (TextView) findViewById(R.id.ev2);
        this.detector = new GestureDetector(this, new MeinGestenListener());
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        this.detector.onTouchEvent(event);
        return super.onTouchEvent(event);
    }


    class MeinGestenListener extends GestureDetector.SimpleOnGestureListener {
        @Override
        public boolean onDown(MotionEvent ev) {
            Log.d(TAG, "onDown ist eingetroffen");
            return true; // muss true sein
        }
        @Override
        public boolean onFling(MotionEvent ev1, MotionEvent ev2, float velX, float velY) {
            tv.setText("Fling Event ist eingetroffen");
            tvx.setText(""+velX);
            tvy.setText(""+velY);
            tv1.setText(ev1.toString());
            tv2.setText(ev2.toString());
            return true;
        }
        @Override
        public boolean onDoubleTap(MotionEvent ev) {
            tv.setText("DoubleTap ist eingetroffen");
            tvx.setText("");
            tvy.setText("");
            tv1.setText("");
            tv2.setText(ev.toString());
            return true;
        }
    }
}
Das Wischen wird durch onFling gefangen. Die beiden float-Parameter geben an, wie stark in X- bzw. Y-Richtung gewischt wurde. Man muss die Werte ein wenig ins Verhältnis setzen, um die korrekte Richtung zu ermitteln. Negative Werte gehen nach links bzw. nach oben.