Gest dotyku występuje, gdy użytkownik zbliży co najmniej 1 palec do ekranu dotykowego, a aplikacja zinterpretuje ten wzorzec dotknięć jako gest. Wykrywanie gestów dzieli się na 2 fazy:
- Zbieram dane zdarzeń dotknięcia.
- Zinterpretowanie danych w celu określenia, czy spełniają kryteria gestów obsługiwanych przez Twoją aplikację.
Zajęcia na AndroidzieX
Przykłady w tym dokumencie używają klas GestureDetectorCompat
i MotionEventCompat
. Znajdziesz je w bibliotece AndroidaX. W miarę możliwości używaj klas AndroidaX, aby zapewnić zgodność z wcześniejszymi urządzeniami.
MotionEventCompat
nie zastępuje klasy MotionEvent
. Udostępnia on statyczne metody narzędzia, do których przekazujesz obiekt MotionEvent
, aby otrzymać działanie powiązane z tym zdarzeniem.
Zbieranie danych
Gdy użytkownik zbliży do ekranu co najmniej 1 palcem, wywoła to wywołanie zwrotne onTouchEvent()
w widoku, w którym odbierają zdarzenia dotknięcia. W przypadku każdej sekwencji zdarzeń dotknięcia (np. pozycji, nacisku, rozmiaru i dodania innego palca), która jest rozpoznawana jako gest, funkcja onTouchEvent()
jest wywoływana kilka razy.
Gest zaczyna się, gdy użytkownik po raz pierwszy dotyka ekranu, jest kontynuowany, ponieważ system śledzi położenie palca użytkownika lub palców, a kończy się na wyjęciu ostatniego palca użytkownika opuszczającego ekran.
W trakcie tej rozmowy strona MotionEvent
wyświetlona na stronie onTouchEvent()
zawiera szczegóły każdej interakcji. Aplikacja może użyć danych dostarczanych przez MotionEvent
, by określić, czy ma miejsce dany gest.
Rejestrowanie zdarzeń dotknięcia w ramach aktywności lub widoku
Aby przechwytywać zdarzenia dotyku w Activity
lub View
, zastąp wywołanie zwrotne onTouchEvent()
.
Ten fragment kodu wykorzystuje parametr getAction()
do wyodrębnienia z parametru event
działania wykonywanego przez użytkownika.
Otrzymasz nieprzetworzone dane, które pozwolą Ci określić, czy gest, na którym Ci zależy.
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 klika, klika, przytrzymuje i przeciąga ten kod, gdy użytkownik klika, klika, przytrzymuje i przeciąga w aplikacji Logcat:
GESTURES D Action was DOWN GESTURES D Action was UP GESTURES D Action was MOVE
Możesz następnie samodzielnie przetwarzać te zdarzenia, aby określić, czy są to gesty, których potrzebujesz. Jeśli jednak Twoja aplikacja używa typowych gestów, takich jak dwukrotne kliknięcie, naciśnięcie i przytrzymanie, przesuwanie itp., możesz skorzystać z klasy GestureDetector
. GestureDetector
ułatwia wykrywanie typowych gestów bez samodzielnego przetwarzania poszczególnych zdarzeń dotknięcia. Jest to szerzej omawiane w sekcji Wykrywanie gestów.
Rejestruj zdarzenia dotknięcia w jednym widoku
Zamiast onTouchEvent()
możesz dołączyć obiekt View.OnTouchListener
do dowolnego obiektu View
za pomocą metody setOnTouchListener()
. Dzięki temu można nasłuchiwać zdarzeń dotknięcia bez podklasyfikowania istniejącego obiektu 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 na utworzenie detektora, który w przypadku zdarzenia ACTION_DOWN
zwróci wartość false
.
Jeśli to zrobisz, detektor nie będzie wywoływany w kolejnych sekwencjach zdarzeń ACTION_MOVE
i ACTION_UP
. Dzieje się tak, ponieważ ACTION_DOWN
jest punktem początkowym
wszystkich zdarzeń dotknięcia.
Jeśli tworzysz widok niestandardowy, możesz zastąpić parametr onTouchEvent()
w sposób opisany powyżej.
Wykrywanie gestów
Android udostępnia klasę GestureDetector
do wykrywania typowych gestów. Obsługuje m.in. gesty onDown()
, onLongPress()
i onFling()
.
Funkcji GestureDetector
możesz używać w połączeniu z opisaną wcześniej metodą onTouchEvent()
.
Wykryj wszystkie obsługiwane gesty
Podczas tworzenia wystąpienia obiektu GestureDetectorCompat
jednym z jego parametrów jest klasa implementująca interfejs GestureDetector.OnGestureListener
. GestureDetector.OnGestureListener
powiadamia użytkowników o konkretnych zdarzeniach dotknięcia. Aby obiekt GestureDetector
mógł otrzymywać zdarzenia, zastąp metodę onTouchEvent()
widoku lub działania i przekaż wszystkie zaobserwowane zdarzenia do instancji wzorca.
W tym fragmencie kodu zwracana wartość true
z poszczególnych metod on<TouchEvent>
wskazuje, że obsługiwane jest zdarzenie dotknięcia. Wartość zwrotna równa false
przekazuje zdarzenia w dół stosu widoków aż do pomyślnego przetworzenia kliknięcia.
Jeśli uruchomisz ten fragment kodu w aplikacji testowej, możesz sprawdzić, jak przebiegają działania podczas interakcji z ekranem dotykowym, i jaka jest zawartość MotionEvent
w przypadku każdego zdarzenia dotknięcia. Zobaczysz, ile danych zostało wygenerowanych na potrzeby 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 zestawu obsługiwanych gestów
Jeśli chcesz przetworzyć tylko kilka gestów, możesz rozwinąć GestureDetector.SimpleOnGestureListener
zamiast implementować interfejs GestureDetector.OnGestureListener
.
GestureDetector.SimpleOnGestureListener
udostępnia implementację wszystkich metod on<TouchEvent>
, ponieważ dla każdej z nich zwraca wartość false
. Pozwala to zastąpić tylko te metody, które są dla Ciebie istotne. Na przykład ten fragment kodu tworzy klasę, która rozszerza zakres GestureDetector.SimpleOnGestureListener
i zastępuje onFling()
oraz onDown()
.
Niezależnie od tego, czy używasz metody 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()
(domyślnie GestureDetector.SimpleOnGestureListener
), system zakłada, że chcesz zignorować pozostałą część gestu, a pozostałe metody metody GestureDetector.OnGestureListener
nie są wywoływane. Może to spowodować nieoczekiwane problemy w aplikacji. Zwrot 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; } } }