Gest dotykowy występuje, gdy użytkownik umieści 1 lub więcej palców na ekranie dotykowym, a Twoja aplikacja zinterpretuje ten wzorzec dotknięć jako gest. Wykrywanie gestów odbywa się w 2 etapach:
- Zbieranie danych o zdarzeniach dotykowych.
- Interpretowanie danych w celu ustalenia, czy spełniają one kryteria dla gestów obsługiwanych przez Twoją aplikację.
Klasy AndroidX
Przykłady w tym dokumencie używają klas GestureDetectorCompat i MotionEventCompat. Te klasy znajdują się w bibliotece AndroidX. Tam, gdzie to możliwe, używaj klas AndroidX, aby zapewnić zgodność ze starszymi urządzeniami.
MotionEventCompat nie zastępuje zajęć
MotionEvent. Zamiast tego udostępnia statyczne metody narzędziowe, do których przekazujesz obiekt MotionEvent, aby otrzymać działanie powiązane z tym zdarzeniem.
Zbieranie danych
Gdy użytkownik umieści 1 lub więcej palców na ekranie, wywoływane jest
wywołanie zwrotne
onTouchEvent()
w widoku, który odbiera zdarzenia dotykowe. W przypadku każdej sekwencji zdarzeń dotykowych – takich jak pozycja, nacisk, rozmiar i dodanie kolejnego palca – która jest identyfikowana jako gest, onTouchEvent() jest wywoływane kilka razy.
Gest zaczyna się, gdy użytkownik po raz pierwszy dotknie ekranu, trwa, gdy system śledzi położenie palca lub palców użytkownika, i kończy się, gdy system zarejestruje ostatnie zdarzenie, czyli gdy ostatni palec użytkownika opuści ekran.
Podczas tej interakcji MotionEvent dostarczane do onTouchEvent() zawiera szczegóły każdej interakcji. Twoja aplikacja może używać danych dostarczonych przez MotionEvent, aby określić, czy wystąpił gest, który Cię interesuje.
Przechwytywanie zdarzeń dotykowych w przypadku aktywności lub widoku
Aby przechwytywać zdarzenia dotykowe w Activity lub View, zastąp wywołanie zwrotne onTouchEvent().
Ten fragment kodu używa getAction(), aby wyodrębnić działanie wykonywane przez użytkownika z parametru event.
Dzięki temu uzyskasz surowe dane potrzebne do określenia, czy wystąpił gest, który Cię interesuje.
Kotlin
class MainActivity : Activity() { ... // This example shows an Activity. You can use the same approach if you are // subclassing a View. override fun onTouchEvent(event: MotionEvent): Boolean { return when (event.action) { MotionEvent.ACTION_DOWN -> { Log.d(DEBUG_TAG, "Action was DOWN") true } MotionEvent.ACTION_MOVE -> { Log.d(DEBUG_TAG, "Action was MOVE") true } MotionEvent.ACTION_UP -> { Log.d(DEBUG_TAG, "Action was UP") true } MotionEvent.ACTION_CANCEL -> { Log.d(DEBUG_TAG, "Action was CANCEL") true } MotionEvent.ACTION_OUTSIDE -> { Log.d(DEBUG_TAG, "Movement occurred outside bounds of current screen element") true } else -> super.onTouchEvent(event) } } }
Java
public class MainActivity extends Activity { ... // This example shows an Activity. You can use the same approach if you are // subclassing a View. @Override public boolean onTouchEvent(MotionEvent event){ switch(event.getAction()) { case (MotionEvent.ACTION_DOWN) : Log.d(DEBUG_TAG,"Action was DOWN"); return true; case (MotionEvent.ACTION_MOVE) : Log.d(DEBUG_TAG,"Action was MOVE"); return true; case (MotionEvent.ACTION_UP) : Log.d(DEBUG_TAG,"Action was UP"); return true; case (MotionEvent.ACTION_CANCEL) : Log.d(DEBUG_TAG,"Action was CANCEL"); return true; case (MotionEvent.ACTION_OUTSIDE) : Log.d(DEBUG_TAG,"Movement occurred outside bounds of current screen element"); return true; default : return super.onTouchEvent(event); } }
Gdy użytkownik będzie klikać, dotykać i przytrzymywać oraz przeciągać, ten kod będzie generować w Logcat komunikaty takie jak te:
GESTURES D Action was DOWN GESTURES D Action was UP GESTURES D Action was MOVE
W przypadku gestów niestandardowych możesz samodzielnie przetwarzać te zdarzenia, aby określić, czy reprezentują one gest, który musisz obsłużyć. Jeśli jednak Twoja aplikacja używa typowych gestów, takich jak dwukrotne dotknięcie, naciśnięcie i przytrzymanie, przeciągnięcie itp., możesz skorzystać z klasy GestureDetector. GestureDetector ułatwia wykrywanie typowych gestów bez samodzielnego przetwarzania poszczególnych zdarzeń dotykowych. Więcej informacji znajdziesz w sekcji Wykrywanie gestów.
Przechwytywanie zdarzeń dotykowych w przypadku pojedynczego widoku
Zamiast onTouchEvent() możesz dołączyć obiekt
View.OnTouchListener
do dowolnego obiektu View
za pomocą metody
setOnTouchListener(). Umożliwia to nasłuchiwanie zdarzeń dotykowych bez tworzenia podklasy istniejącego View, jak pokazano w tym przykładzie:
Kotlin
findViewById<View>(R.id.my_view).setOnTouchListener { v, event -> // Respond to touch events. true }
Java
View myView = findViewById(R.id.my_view); myView.setOnTouchListener(new OnTouchListener() { public boolean onTouch(View v, MotionEvent event) { // Respond to touch events. return true; } });
Uważaj, aby nie tworzyć detektora, który zwraca false w przypadku zdarzenia ACTION_DOWN.
Jeśli to zrobisz, detektor nie zostanie wywołany w przypadku kolejnej sekwencji zdarzeń ACTION_MOVE i ACTION_UP. Dzieje się tak, ponieważ ACTION_DOWN jest punktem początkowym wszystkich zdarzeń dotykowych.
Jeśli tworzysz widok niestandardowy, możesz zastąpić onTouchEvent(), jak opisano wcześniej.
Wykrywanie gestów
Android udostępnia klasę GestureDetector do wykrywania typowych gestów. Obsługuje ona m.in. gesty onDown(), onLongPress() i onFling().
Możesz używać GestureDetector w połączeniu z opisaną wcześniej metodą onTouchEvent().
Wykrywanie wszystkich obsługiwanych gestów
Gdy tworzysz instancję obiektu GestureDetectorCompat, jednym z parametrów jest klasa, która implementuje interfejs GestureDetector.OnGestureListener. GestureDetector.OnGestureListener powiadamia użytkowników o wystąpieniu określonego zdarzenia dotknięcia. Aby obiekt GestureDetector mógł odbierać zdarzenia, zastąp metodę onTouchEvent() widoku lub aktywności i przekaż wszystkie zaobserwowane zdarzenia do instancji detektora.
W tym fragmencie kodu wartość zwracana true z poszczególnych on<TouchEvent> metod oznacza, że zdarzenie dotknięcia zostało obsłużone. Wartość zwracana false przekazuje zdarzenia w dół stosu widoków, aż dotknięcie zostanie pomyślnie obsłużone.
Jeśli uruchomisz ten fragment kodu w aplikacji testowej, możesz sprawdzić, jak wywoływane są działania, gdy wchodzisz w interakcję z ekranem dotykowym, oraz jaka jest zawartość MotionEvent w przypadku każdego zdarzenia dotknięcia. Następnie zobaczysz, ile danych jest generowanych w przypadku prostych interakcji.
Kotlin
private const val DEBUG_TAG = "Gestures" class MainActivity : Activity(), GestureDetector.OnGestureListener, GestureDetector.OnDoubleTapListener { private lateinit var mDetector: GestureDetectorCompat // Called when the activity is first created. public override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // Instantiate the gesture detector with the // application context and an implementation of // GestureDetector.OnGestureListener. mDetector = GestureDetectorCompat(this, this) // Set the gesture detector as the double-tap // listener. mDetector.setOnDoubleTapListener(this) } override fun onTouchEvent(event: MotionEvent): Boolean { return if (mDetector.onTouchEvent(event)) { true } else { super.onTouchEvent(event) } } override fun onDown(event: MotionEvent): Boolean { Log.d(DEBUG_TAG, "onDown: $event") return true } override fun onFling( event1: MotionEvent, event2: MotionEvent, velocityX: Float, velocityY: Float ): Boolean { Log.d(DEBUG_TAG, "onFling: $event1 $event2") return true } override fun onLongPress(event: MotionEvent) { Log.d(DEBUG_TAG, "onLongPress: $event") } override fun onScroll( event1: MotionEvent, event2: MotionEvent, distanceX: Float, distanceY: Float ): Boolean { Log.d(DEBUG_TAG, "onScroll: $event1 $event2") return true } override fun onShowPress(event: MotionEvent) { Log.d(DEBUG_TAG, "onShowPress: $event") } override fun onSingleTapUp(event: MotionEvent): Boolean { Log.d(DEBUG_TAG, "onSingleTapUp: $event") return true } override fun onDoubleTap(event: MotionEvent): Boolean { Log.d(DEBUG_TAG, "onDoubleTap: $event") return true } override fun onDoubleTapEvent(event: MotionEvent): Boolean { Log.d(DEBUG_TAG, "onDoubleTapEvent: $event") return true } override fun onSingleTapConfirmed(event: MotionEvent): Boolean { Log.d(DEBUG_TAG, "onSingleTapConfirmed: $event") return true } }
Java
public class MainActivity extends Activity implements GestureDetector.OnGestureListener, GestureDetector.OnDoubleTapListener{ private static final String DEBUG_TAG = "Gestures"; private GestureDetectorCompat mDetector; // Called when the activity is first created. @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Instantiate the gesture detector with the // application context and an implementation of // GestureDetector.OnGestureListener. mDetector = new GestureDetectorCompat(this,this); // Set the gesture detector as the double-tap // listener. mDetector.setOnDoubleTapListener(this); } @Override public boolean onTouchEvent(MotionEvent event){ if (this.mDetector.onTouchEvent(event)) { return true; } return super.onTouchEvent(event); } @Override public boolean onDown(MotionEvent event) { Log.d(DEBUG_TAG,"onDown: " + event.toString()); return true; } @Override public boolean onFling(MotionEvent event1, MotionEvent event2, float velocityX, float velocityY) { Log.d(DEBUG_TAG, "onFling: " + event1.toString() + event2.toString()); return true; } @Override public void onLongPress(MotionEvent event) { Log.d(DEBUG_TAG, "onLongPress: " + event.toString()); } @Override public boolean onScroll(MotionEvent event1, MotionEvent event2, float distanceX, float distanceY) { Log.d(DEBUG_TAG, "onScroll: " + event1.toString() + event2.toString()); return true; } @Override public void onShowPress(MotionEvent event) { Log.d(DEBUG_TAG, "onShowPress: " + event.toString()); } @Override public boolean onSingleTapUp(MotionEvent event) { Log.d(DEBUG_TAG, "onSingleTapUp: " + event.toString()); return true; } @Override public boolean onDoubleTap(MotionEvent event) { Log.d(DEBUG_TAG, "onDoubleTap: " + event.toString()); return true; } @Override public boolean onDoubleTapEvent(MotionEvent event) { Log.d(DEBUG_TAG, "onDoubleTapEvent: " + event.toString()); return true; } @Override public boolean onSingleTapConfirmed(MotionEvent event) { Log.d(DEBUG_TAG, "onSingleTapConfirmed: " + event.toString()); return true; } }
Wykrywanie podzbioru obsługiwanych gestów
Jeśli chcesz przetwarzać tylko kilka gestów, możesz rozszerzyć GestureDetector.SimpleOnGestureListener zamiast implementować interfejs GestureDetector.OnGestureListener.
GestureDetector.SimpleOnGestureListener udostępnia
implementację wszystkich metod
on<TouchEvent>, zwracając dla nich wartość
false. Dzięki temu możesz zastąpić tylko te metody, które Cię interesują. Na przykład ten fragment kodu tworzy klasę, która rozszerza GestureDetector.SimpleOnGestureListener i zastępuje onFling() oraz onDown().
Niezależnie od tego, czy używasz GestureDetector.OnGestureListener, czy GestureDetector.SimpleOnGestureListener, warto zaimplementować metodę onDown(), która zwraca true. Dzieje się tak, ponieważ wszystkie gesty zaczynają się od komunikatu onDown(). Jeśli zwrócisz false z onDown(), tak jak domyślnie robi to GestureDetector.SimpleOnGestureListener, system założy, że chcesz zignorować resztę gestu, a inne metody GestureDetector.OnGestureListener nie zostaną wywołane. Może to spowodować nieoczekiwane problemy w Twojej aplikacji. Zwracaj false z onDown() tylko wtedy, gdy naprawdę chcesz zignorować cały gest.
Kotlin
private const val DEBUG_TAG = "Gestures" class MainActivity : Activity() { private lateinit var mDetector: GestureDetectorCompat public override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) mDetector = GestureDetectorCompat(this, MyGestureListener()) } override fun onTouchEvent(event: MotionEvent): Boolean { mDetector.onTouchEvent(event) return super.onTouchEvent(event) } private class MyGestureListener : GestureDetector.SimpleOnGestureListener() { override fun onDown(event: MotionEvent): Boolean { Log.d(DEBUG_TAG, "onDown: $event") return true } override fun onFling( event1: MotionEvent, event2: MotionEvent, velocityX: Float, velocityY: Float ): Boolean { Log.d(DEBUG_TAG, "onFling: $event1 $event2") return true } } }
Java
public class MainActivity extends Activity { private GestureDetectorCompat mDetector; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mDetector = new GestureDetectorCompat(this, new MyGestureListener()); } @Override public boolean onTouchEvent(MotionEvent event){ if (this.mDetector.onTouchEvent(event)) { return true; } return super.onTouchEvent(event); } class MyGestureListener extends GestureDetector.SimpleOnGestureListener { private static final String DEBUG_TAG = "Gestures"; @Override public boolean onDown(MotionEvent event) { Log.d(DEBUG_TAG,"onDown: " + event.toString()); return true; } @Override public boolean onFling(MotionEvent event1, MotionEvent event2, float velocityX, float velocityY) { Log.d(DEBUG_TAG, "onFling: " + event1.toString() + event2.toString()); return true; } } }