Z tej lekcji dowiesz się, jak śledzić ruch w przypadku zdarzeń dotyku.
Za każdym razem, gdy zmienia się bieżąca pozycja kontaktu, naciskanie lub rozmiar kontaktu dotykowego, wywoływany jest nowy obiekt onTouchEvent()
wraz ze zdarzeniem ACTION_MOVE
. Jak opisano w sekcji Wykrywanie typowych gestów, wszystkie te zdarzenia są rejestrowane w parametrze MotionEvent
onTouchEvent()
.
Ponieważ dotyk palców nie zawsze jest najbardziej precyzyjną formą interakcji, wykrywanie zdarzeń dotknięcia często opiera się na ruchu, a nie na prostym kontakcie. Aby ułatwić aplikacjom odróżnienie gestów opartych na ruchu (np. przesuwania) od gestów niezwiązanych z ruchem (np. pojedynczego kliknięcia), Android stosuje m.in. pojęcie płynięcia dotykiem. Sygnalizowanie dotknięcia to odległość wyrażona w pikselach, na której może się przemieszczać użytkownik dotknięcia, zanim gest zostanie zinterpretowany jako gest związany z ruchem. Więcej informacji na ten temat znajdziesz w artykule Zarządzanie zdarzeniami dotknięcia w grupie ViewGroup.
W zależności od potrzeb aplikacji można śledzić ruch na kilka sposobów. Oto przykłady:
- Pozycja początkowa i końcowa wskaźnika, na przykład przesunięcie obiektu na ekranie z punktu A do punktu B.
- Kierunek, w którym porusza się wskaźnik, określony za pomocą współrzędnych X i Y.
- Historia. Rozmiar historii gestu możesz sprawdzić, wywołując metodę
MotionEvent
getHistorySize()
. Korzystając z metodgetHistorical<Value>
zdarzeń ruchu, możesz następnie ustalić położenie, rozmiar, czas i siłę nacisku każdego zdarzenia historycznego. Historia jest przydatna podczas renderowania śladu palca użytkownika, na przykład przy rysowaniu dotykiem. Szczegółowe informacje znajdziesz w dokumentacjiMotionEvent
. - Prędkość wskaźnika poruszającego się po ekranie dotykowym.
Zapoznaj się z tymi powiązanymi materiałami:
Śledź prędkość
Możesz wykonać gest oparty na ruchu oparty na odległości lub kierunku, którym porusza się wskaźnik. Jednak często jest ona czynnikiem decydującym o śledzeniu właściwości gestu lub podejmowaniu decyzji, czy został on wykonany. Aby ułatwić obliczanie szybkości, Android udostępnia klasę VelocityTracker
.
VelocityTracker
pomaga śledzić szybkość zdarzeń dotyku. Przydaje się to w przypadku gestów, w przypadku których jednym z kryteriów gestu jest prędkość, np. rzut.
Oto przykład, który ilustruje przeznaczenie metod w interfejsie API VelocityTracker
:
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żyj przechwytywania wskaźnika
Niektóre aplikacje, takie jak gry, pulpit zdalny i klienty wirtualizacji, czerpią korzyści z kontroli nad wskaźnikiem myszy. Przechwytywanie wskaźników to funkcja dostępna w Androidzie 8.0 (poziom interfejsu API 26) i nowszych, która zapewnia tę kontrolę, wyświetlając wszystkie zdarzenia myszy w widocznym obszarze aplikacji.
Przechwytywanie wskaźnika żądania
Widok w aplikacji może żądać przechwytywania wskaźnika tylko wtedy, gdy hierarchia widoków, która go zawiera, jest zaznaczona. Dlatego wysyłaj żądanie przechwytywania wskaźnika, gdy w widoku występuje określone działanie użytkownika, np. podczas zdarzenia onClick()
lub w module obsługi zdarzeń onWindowFocusChanged()
w Twojej aktywności.
Aby zażądać przechwycenia wskaźnika, wywołaj w widoku metodę requestPointerCapture()
. Poniższy przykładowy kod pokazuje, jak zażądać 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 zostanie zrealizowane, Android wywoła metodę onPointerCaptureChange(true)
.
System dostarcza zdarzenia myszy do widoku aktywnego w aplikacji, o ile znajduje się on w tej samej hierarchii widoków co widok, który zażądał przechwycenia. Do czasu opublikowania zapisu inne aplikacje, w tym zdarzenia ACTION_OUTSIDE
, przestaną otrzymywać zdarzenia myszy. Android w zwykły sposób przesyła zdarzenia wskaźnika ze źródeł innych niż mysz, ale wskaźnik myszy nie jest już widoczny.
Obsługa zarejestrowanych zdarzeń wskaźnika
Gdy widok uzyska przechwycony wskaźnik, Android dostarczy zdarzenia myszy. Zaznaczony widok 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ładowy kod pokazuje, jak wdrożyć 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ładowy kod 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 korzystasz z widoku niestandardowego, czy rejestrujesz detektor, Twój widok otrzymuje MotionEvent
ze współrzędnymi wskaźnika, które określają ruchy względne, takie jak delta X lub Y, podobne do współrzędnych dostarczanych przez kulkę. Współrzędne możesz pobrać za pomocą funkcji getX()
i getY()
.
Przechwytywanie wskaźnika wersji
Widok w aplikacji może uruchamiać przechwytywanie wskaźnika, wywołując metodę releasePointerCapture()
, jak pokazano w tym przykładowym kodzie:
Kotlin
override fun onClick(view: View) { view.releasePointerCapture() }
Java
@Override public void onClick(View view) { view.releasePointerCapture(); }
System może cofnąć przechwycony widok bez Twojego wywołania releasePointerCapture()
. Zwykle jest to spowodowane tym, że hierarchia widoków zawierająca widok, który przechwytywano żądania, przestaje być aktywny.