Жест – это касание сенсорного экрана одним или несколькими пальцами, которое приложение интерпретирует как определенное действие. Распознавание жестов состоит из двух этапов:
- Сбор данных о событиях касания.
- Интерпретация данных для определения того, соответствуют ли они критериям жестов, поддерживаемых вашим приложением.
Классы AndroidX
В примерах из этого документа используются классы GestureDetectorCompat и MotionEventCompat. Эти классы находятся в библиотеке AndroidX. По возможности используйте классы AndroidX, чтобы обеспечить совместимость с более ранними устройствами.
MotionEventCompat не заменяет класс MotionEvent. Он предоставляет статические вспомогательные методы, которым вы передаете объект MotionEvent, чтобы получить действие, связанное с этим событием.
Сбор данных
Когда пользователь касается экрана одним или несколькими пальцами, в представлении, которое получает события касания, активируется обратный вызов onTouchEvent(). Для каждой последовательности событий касания (например, положение, давление, размер и добавление другого пальца), которая идентифицируется как жест, onTouchEvent() запускается несколько раз.
Жест начинается, когда пользователь впервые касается экрана, продолжается, пока система отслеживает положение пальца или пальцев пользователя, и заканчивается, когда последний палец пользователя покидает экран.
В течение всего взаимодействия MotionEvent, отправляемый в onTouchEvent(), предоставляет подробные сведения о каждом взаимодействии. Приложение может использовать данные, предоставленные MotionEvent, чтобы определить, было ли выполнено нужное жесту действие.
Как регистрировать события касания для Activity или View
Чтобы перехватывать события касания в Activity или View, переопределите обратный вызов onTouchEvent().
В приведенном ниже фрагменте кода используется getAction(), чтобы извлечь действие, которое выполняет пользователь, из параметра event.
Это позволяет получить необработанные данные, необходимые для определения того, было ли выполнено нужное вам действие.
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); } }
Этот код создает в Logcat сообщения, подобные приведенным ниже, когда пользователь нажимает, касается и удерживает, а также перетаскивает:
GESTURES D Action was DOWN GESTURES D Action was UP GESTURES D Action was MOVE
Для специальных жестов вы можете самостоятельно обрабатывать эти события, чтобы определить, представляют ли они нужный вам жест. Однако если в вашем приложении используются стандартные жесты, такие как двойное нажатие, нажатие и удержание, пролистывание и т. д., вы можете воспользоваться классом GestureDetector. GestureDetector упрощает распознавание распространенных жестов, избавляя вас от необходимости обрабатывать отдельные события касания. Подробнее об этом рассказывается в разделе Обнаружение жестов.
Как регистрировать события касания для одного представления
Вместо onTouchEvent() можно прикрепить объект View.OnTouchListener к любому объекту View с помощью метода setOnTouchListener(). Это позволяет прослушивать события касания без создания подкласса существующего View, как показано в следующем примере:
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; } });
Не создавайте прослушиватель, который возвращает false для события ACTION_DOWN.
Если вы это сделаете, прослушиватель не будет вызываться для последующей последовательности событий ACTION_MOVE и ACTION_UP. Потому что ACTION_DOWN – это начальная точка для всех событий касания.
Если вы создаете собственное представление, то можете переопределить onTouchEvent(), как описано выше.
Распознавать жесты
В Android есть класс GestureDetector, который позволяет распознавать распространенные жесты. Поддерживаются следующие жесты:onDown(),onLongPress()иonFling().
Вы можете использовать GestureDetector вместе с методом onTouchEvent(), описанным выше.
Распознавать все поддерживаемые жесты
При создании экземпляра объекта GestureDetectorCompat одним из параметров, которые он принимает, является класс, реализующий интерфейс GestureDetector.OnGestureListener. GestureDetector.OnGestureListener уведомляет пользователей, когда происходит определенное событие касания. Чтобы объект GestureDetector мог получать события, переопределите метод onTouchEvent() представления или действия и передайте все наблюдаемые события экземпляру детектора.
В приведенном ниже фрагменте кода возвращаемое значение true из отдельных методов on<TouchEvent> указывает, что событие касания обработано. Возвращаемое значение false передает события вниз по стеку представлений, пока не будет успешно обработано касание.
Если вы запустите следующий фрагмент кода в тестовом приложении, то сможете понять, как активируются действия при взаимодействии с сенсорным экраном и что содержится в MotionEvent для каждого события касания. Вы увидите, сколько данных генерируется при простых взаимодействиях.
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; } }
Распознавать некоторые поддерживаемые жесты
Если вам нужно обрабатывать только несколько жестов, вы можете расширить класс GestureDetector.SimpleOnGestureListener вместо того, чтобы реализовывать интерфейс GestureDetector.OnGestureListener.
GestureDetector.SimpleOnGestureListener предоставляет реализацию для всех методов on<TouchEvent>, возвращая false для каждого из них. Это позволяет переопределять только те методы, которые вам нужны. Например, в приведенном ниже фрагменте кода создается класс, который расширяет GestureDetector.SimpleOnGestureListener и переопределяет onFling() и onDown().
Независимо от того, используете ли вы GestureDetector.OnGestureListener или GestureDetector.SimpleOnGestureListener, рекомендуется реализовать метод onDown(), который возвращает true. Это связано с тем, что все жесты начинаются с сообщения onDown(). Если вы возвращаете false из onDown(), как это делает GestureDetector.SimpleOnGestureListener по умолчанию, система предполагает, что вы хотите игнорировать остальную часть жеста, и другие методы GestureDetector.OnGestureListener не вызываются. Это может привести к непредвиденным проблемам в приложении. Возвращайте false из onDown(), только если вы действительно хотите игнорировать весь жест.
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; } } }