Z tej lekcji dowiesz się, jak śledzić ruch w zdarzeniach dotykowych.
Nowe zdarzenie
onTouchEvent()
jest wywoływane przez zdarzenie
ACTION_MOVE
za każdym razem, gdy zmieni się bieżąca pozycja, nacisk lub rozmiar kontaktu dotykowego. Jak opisano w artykule Wykrywanie typowych gestów, wszystkie te zdarzenia są rejestrowane w parametrze MotionEvent zdarzenia onTouchEvent().
Dotyk palcem nie zawsze jest najdokładniejszą formą interakcji, dlatego wykrywanie zdarzeń dotykowych często opiera się bardziej na ruchu niż na prostym kontakcie. Aby pomóc aplikacjom w odróżnianiu gestów opartych na ruchu (takich jak przesunięcie) od gestów nieopartych na ruchu (takich jak pojedyncze kliknięcie), Android wprowadza pojęcie odchylenia dotyku. Tolerancja dotyku to odległość w pikselach, o którą może się przesunąć dotyk użytkownika, zanim gest zostanie zinterpretowany jako gest oparty na ruchu. Więcej informacji na ten temat znajdziesz w artykule Zarządzanie zdarzeniami dotykowymi w obiekcie ViewGroup.
Ruch w geście można śledzić na kilka sposobów, w zależności od potrzeb aplikacji. Przykłady:
- pozycję początkową i końcową wskaźnika, np. podczas przenoszenia obiektu na ekranie z punktu A do punktu B;
- Kierunek, w którym porusza się wskaźnik, określony przez współrzędne X i Y.
- Historia. Rozmiar historii gestów możesz sprawdzić, wywołując metodę
MotionEventgetHistorySize(). Następnie możesz uzyskać pozycje, rozmiary, czas i nacisk każdego z wydarzeń historycznych, korzystając z metodgetHistorical<Value>zdarzenia ruchu. Historia jest przydatna podczas renderowania śladu palca użytkownika, np. podczas rysowania za pomocą dotyku. Szczegółowe informacje znajdziesz wMotionEvent. - Szybkość przesuwania wskaźnika po ekranie dotykowym.
Zapoznaj się z tymi powiązanymi materiałami:
- Omówienie zdarzeń wejściowych
- Omówienie czujników
- Umożliwianie interakcji z widokiem niestandardowym
Prędkość ścieżki
Możesz używać gestów opartych na ruchu, które zależą od odległości lub kierunku, w jakim przesuwa się wskaźnik. Szybkość jest jednak często czynnikiem decydującym o śledzeniu charakterystyki gestu lub o tym, czy gest został wykonany. Aby ułatwić obliczanie prędkości, Android udostępnia klasę VelocityTracker.
VelocityTracker pomaga śledzić szybkość zdarzeń dotyku. Jest to przydatne w przypadku gestów, w których prędkość jest częścią kryteriów, np. w przypadku szybkiego przesunięcia.
Oto przykład, który ilustruje przeznaczenie metod w interfejsie VelocityTracker API:
Kotlin
private const val DEBUG_TAG = "Velocity" class MainActivity : Activity() { private var mVelocityTracker: VelocityTracker? = null override fun onTouchEvent(event: MotionEvent): Boolean { when (event.actionMasked) { MotionEvent.ACTION_DOWN -> { // Reset the velocity tracker back to its initial state. mVelocityTracker?.clear() // If necessary, retrieve a new VelocityTracker object to watch // the velocity of a motion. mVelocityTracker = mVelocityTracker ?: VelocityTracker.obtain() // Add a user's movement to the tracker. mVelocityTracker?.addMovement(event) } MotionEvent.ACTION_MOVE -> { mVelocityTracker?.apply { val pointerId: Int = event.getPointerId(event.actionIndex) addMovement(event) // When you want to determine the velocity, call // computeCurrentVelocity(). Then, call getXVelocity() and // getYVelocity() to retrieve the velocity for each pointer // ID. computeCurrentVelocity(1000) // Log velocity of pixels per second. It's best practice to // use VelocityTrackerCompat where possible. Log.d("", "X velocity: ${getXVelocity(pointerId)}") Log.d("", "Y velocity: ${getYVelocity(pointerId)}") } } MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> { // Return a VelocityTracker object back to be re-used by others. mVelocityTracker?.recycle() mVelocityTracker = null } } return true } }
Java
public class MainActivity extends Activity { private static final String DEBUG_TAG = "Velocity"; ... private VelocityTracker mVelocityTracker = null; @Override public boolean onTouchEvent(MotionEvent event) { int index = event.getActionIndex(); int action = event.getActionMasked(); int pointerId = event.getPointerId(index); switch(action) { case MotionEvent.ACTION_DOWN: if(mVelocityTracker == null) { // Retrieve a new VelocityTracker object to watch the // velocity of a motion. mVelocityTracker = VelocityTracker.obtain(); } else { // Reset the velocity tracker back to its initial state. mVelocityTracker.clear(); } // Add a user's movement to the tracker. mVelocityTracker.addMovement(event); break; case MotionEvent.ACTION_MOVE: mVelocityTracker.addMovement(event); // When you want to determine the velocity, call // computeCurrentVelocity(). Then call getXVelocity() and // getYVelocity() to retrieve the velocity for each pointer ID. mVelocityTracker.computeCurrentVelocity(1000); // Log velocity of pixels per second. It's best practice to use // VelocityTrackerCompat where possible. Log.d("", "X velocity: " + mVelocityTracker.getXVelocity(pointerId)); Log.d("", "Y velocity: " + mVelocityTracker.getYVelocity(pointerId)); break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: // Return a VelocityTracker object back to be re-used by others. mVelocityTracker.recycle(); break; } return true; } }
Używanie przechwytywania wskaźnika
Niektóre aplikacje, takie jak gry, klienty pulpitu zdalnego i wirtualizacji, korzystają z możliwości sterowania wskaźnikiem myszy. Przechwytywanie wskaźnika to funkcja dostępna w Androidzie 8.0 (interfejs API na poziomie 26) i nowszych, która zapewnia tę kontrolę, przekazując wszystkie zdarzenia myszy do widoku w aplikacji, na którym jest fokus.
Żądanie przechwycenia wskaźnika
Widok w aplikacji może poprosić o przechwycenie wskaźnika tylko wtedy, gdy hierarchia widoków, która go zawiera, jest aktywna. Z tego powodu poproś o przechwycenie wskaźnika, gdy w widoku wystąpi określone działanie użytkownika, np. podczas zdarzenia onClick() lub w procedurze obsługi zdarzenia onWindowFocusChanged() w aktywności.
Aby poprosić o przechwycenie wskaźnika, wywołaj metodę requestPointerCapture() w widoku. Poniższy przykład kodu pokazuje, jak wysłać żądanie przechwytywania wskaźnika, gdy użytkownik kliknie widok:
Kotlin
fun onClick(view: View) { view.requestPointerCapture() }
Java
@Override public void onClick(View view) { view.requestPointerCapture(); }
Gdy żądanie przechwycenia wskaźnika zakończy się powodzeniem, Android wywoła funkcję
onPointerCaptureChange(true).
System dostarcza zdarzenia myszy do widoku, na którym jest fokus, w aplikacji, o ile znajduje się on w tej samej hierarchii widoków co widok, który zażądał przechwytywania. Inne aplikacje przestają otrzymywać zdarzenia myszy do momentu zwolnienia przechwytywania, w tym zdarzenia ACTION_OUTSIDE. Android dostarcza zdarzenia wskaźnika z innych źródeł niż mysz w normalny sposób, ale wskaźnik myszy nie jest już widoczny.
Obsługa przechwyconych zdarzeń wskaźnika
Gdy widokowi uda się przejąć przechwytywanie wskaźnika, Android dostarczy zdarzenia myszy. Widok skupiony może obsługiwać zdarzenia, wykonując jedno z tych zadań:
- Jeśli używasz widoku niestandardowego, zastąp
onCapturedPointerEvent(MotionEvent). - W przeciwnym razie zarejestruj
OnCapturedPointerListener.
Poniższy przykład kodu pokazuje, jak zaimplementować onCapturedPointerEvent(MotionEvent):
Kotlin
override fun onCapturedPointerEvent(motionEvent: MotionEvent): Boolean { // Get the coordinates required by your app. val verticalOffset: Float = motionEvent.y // Use the coordinates to update your view and return true if the event is // successfully processed. return true }
Java
@Override public boolean onCapturedPointerEvent(MotionEvent motionEvent) { // Get the coordinates required by your app. float verticalOffset = motionEvent.getY(); // Use the coordinates to update your view and return true if the event is // successfully processed. return true; }
Poniższy przykład kodu pokazuje, jak zarejestrować OnCapturedPointerListener:
Kotlin
myView.setOnCapturedPointerListener { view, motionEvent -> // Get the coordinates required by your app. val horizontalOffset: Float = motionEvent.x // Use the coordinates to update your view and return true if the event is // successfully processed. true }
Java
myView.setOnCapturedPointerListener(new View.OnCapturedPointerListener() { @Override public boolean onCapturedPointer (View view, MotionEvent motionEvent) { // Get the coordinates required by your app. float horizontalOffset = motionEvent.getX(); // Use the coordinates to update your view and return true if the event is // successfully processed. return true; } });
Niezależnie od tego, czy używasz widoku niestandardowego, czy rejestrujesz odbiornik, Twój widok otrzymuje obiekt MotionEvent ze współrzędnymi wskaźnika, które określają ruchy względne, takie jak różnice w wartościach X lub Y, podobnie jak współrzędne dostarczane przez trackball. Współrzędne możesz pobrać za pomocą funkcji getX() i getY().
Zwalnianie przechwytywania wskaźnika
Widok w aplikacji może zwolnić przechwytywanie wskaźnika, wywołując funkcję releasePointerCapture(), jak pokazano w tym przykładzie kodu:
Kotlin
override fun onClick(view: View) { view.releasePointerCapture() }
Java
@Override public void onClick(View view) { view.releasePointerCapture(); }
System może usunąć przechwytywanie z widoku bez wyraźnego wywołania przez Ciebie funkcji releasePointerCapture(), zwykle dlatego, że hierarchia widoków zawierająca widok, który prosi o przechwytywanie, traci fokus.