В этом уроке описывается, как отслеживать движение в событиях касания.
Новый объект onTouchEvent() активируется событием ACTION_MOVE, когда меняется положение, сила нажатия или размер текущего касания. Как описано в разделе Распознавание распространенных жестов, все эти события записываются в параметр MotionEvent метода onTouchEvent().
Поскольку касание пальцем не всегда является самым точным способом взаимодействия, обнаружение событий касания часто основано больше на движении, чем на простом контакте. Чтобы приложения могли различать жесты, основанные на движении (например, пролистывание), и жесты без движения (например, одно нажатие), в Android используется понятие допустимого отклонения при касании. Допустимое отклонение – это расстояние в пикселях, на которое может сместиться палец пользователя, прежде чем жест будет интерпретирован как движение. Дополнительную информацию по этой теме можно найти в статье Управление событиями касания в ViewGroup.
Есть несколько способов отслеживать движение в жесте, в зависимости от потребностей вашего приложения. Примеры:
- Начальное и конечное положение указателя, например при перемещении объекта на экране из точки А в точку Б.
- Направление движения указателя, определяемое координатами X и Y.
- История. Чтобы узнать размер истории жеста, вызовите метод
MotionEventgetHistorySize(). Затем вы можете получить позиции, размеры, время и давление каждого из исторических событий, используя методыgetHistorical<Value>события движения. История полезна, когда нужно отобразить след от пальца пользователя, например при рисовании на сенсорном экране. Подробную информацию вы найдете вMotionEvent. - Скорость перемещения указателя по сенсорному экрану.
Ознакомьтесь со следующей информацией по теме:
Скорость трека
Вы можете создать жест, основанный на расстоянии или направлении перемещения указателя. Однако скорость часто является определяющим фактором при отслеживании характеристик жеста или принятии решения о том, произошел ли жест. Чтобы упростить расчет скорости, Android предоставляет класс VelocityTracker.
VelocityTracker помогает отслеживать скорость событий касания. Это полезно для жестов, в которых скорость является частью критериев жеста, например, броска.
Ниже приведен пример, иллюстрирующий назначение методов в 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; } }
Использовать захват указателя
Некоторым приложениям, например играм, клиентам удаленного рабочего стола и виртуализации, требуется управление указателем мыши. Захват указателя – это функция, доступная в Android 8.0 (уровень API 26) и более поздних версиях. Она позволяет передавать все события мыши в активное окно приложения.
Запросить захват указателя
Представление в приложении может запрашивать захват указателя, только когда иерархия представлений, которая его содержит, находится в фокусе. Поэтому запрашивайте захват указателя, когда пользователь выполняет определенное действие в представлении, например во время события onClick() или в обработчике событий onWindowFocusChanged() вашего действия.
Чтобы запросить захват указателя, вызовите метод requestPointerCapture() в представлении. В следующем примере кода показано, как запросить захват указателя, когда пользователь нажимает на представление:
Kotlin
fun onClick(view: View) { view.requestPointerCapture() }
Java
@Override public void onClick(View view) { view.requestPointerCapture(); }
После успешного запроса на захват указателя Android вызывает onPointerCaptureChange(true).
Система передает события мыши в представление, находящееся в фокусе, если оно находится в той же иерархии, что и представление, запросившее захват. Другие приложения перестают получать события мыши, пока захват не будет отменен, включая события ACTION_OUTSIDE. Android передает события указателя от источников, отличных от мыши, как обычно, но указатель мыши больше не виден.
Обработка событий захваченного указателя
После того как представление успешно захватит указатель, Android доставит события мыши. Представление, находящееся в фокусе, может обрабатывать события, выполняя одну из следующих задач:
- Если вы используете специальный вид, переопределите
onCapturedPointerEvent(MotionEvent). - В противном случае зарегистрируйте
OnCapturedPointerListener.
В примере кода ниже показано, как реализовать 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; }
В примере кода ниже показано, как зарегистрировать 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; } });
Независимо от того, используете ли вы пользовательское представление или регистрируете прослушиватель, ваше представление получает MotionEvent с координатами указателя, которые указывают относительные перемещения, такие как дельты X или Y, аналогично координатам, предоставляемым устройством трекбола. Получить координаты можно с помощью методов getX() и getY().
Отменить захват указателя
Представление в приложении может отменить захват указателя, вызвав метод releasePointerCapture(), как показано в следующем примере кода:
Kotlin
override fun onClick(view: View) { view.releasePointerCapture() }
Java
@Override public void onClick(View view) { view.releasePointerCapture(); }
Система может отменить захват без явного вызова releasePointerCapture(). Обычно это происходит, когда иерархия View, содержащая View, которая запрашивает захват, теряет фокус.