Сенсорный жест возникает, когда пользователь кладет один или несколько пальцев на сенсорный экран, и ваше приложение интерпретирует эту схему касаний как жест. Обнаружение жестов состоит из двух этапов:
- Сбор данных о событиях касания.
- Интерпретация данных, чтобы определить, соответствуют ли они критериям жестов, поддерживаемых вашим приложением.
Классы AndroidX
В примерах в этом документе используются классы GestureDetectorCompat
и MotionEventCompat
. Эти классы находятся в библиотеке AndroidX . По возможности используйте классы AndroidX, чтобы обеспечить совместимость с более ранними устройствами. MotionEventCompat
не является заменой класса MotionEvent
. Скорее, он предоставляет статические служебные методы, которым вы передаете объект MotionEvent
для получения действия, связанного с этим событием.
Сбор данных
Когда пользователь касается экрана одним или несколькими пальцами, это запускает обратный вызов onTouchEvent()
для представления, которое получает события касания. Для каждой последовательности событий касания, такой как положение, давление, размер и добавление другого пальца, которая идентифицируется как жест, onTouchEvent()
запускается несколько раз.
Жест начинается, когда пользователь впервые касается экрана, продолжается, пока система отслеживает положение пальца или пальцев пользователя, и заканчивается фиксацией последнего события, когда последний палец пользователя покидает экран. В ходе этого взаимодействия MotionEvent
, передаваемый в onTouchEvent()
предоставляет подробную информацию о каждом взаимодействии. Ваше приложение может использовать данные, предоставленные MotionEvent
чтобы определить, происходит ли нужный ему жест.
Захват событий касания для действия или представления
Чтобы перехватить события касания в Activity
или View
, переопределите обратный вызов onTouchEvent()
.
В следующем фрагменте кода getAction()
используется для извлечения действия, выполняемого пользователем, из параметра event
. Это дает вам необработанные данные, необходимые для определения того, происходит ли интересующий вас жест.
Котлин
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) } } }
Ява
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
, как показано в следующем примере:
Котлин
findViewById<View>(R.id.my_view).setOnTouchListener { v, event -> // Respond to touch events. true }
Ява
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
для каждого события касания. Затем вы увидите, сколько данных генерируется для простых взаимодействий.
Котлин
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 } }
Ява
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()
только в том случае, если вы действительно хотите игнорировать весь жест.
Котлин
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 } } }
Ява
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; } } }