Detect common gestures
Stay organized with collections
Save and categorize content based on your preferences.
A touch gesture occurs when a user places one or more fingers on the touchscreen and your app interprets this pattern of touches as a gesture. There are two phases to gesture detection:
- Gathering touch event data.
- Interpreting the data to determine whether it meets the criteria for the gestures your app supports.
AndroidX classes
The examples in this document use the
GestureDetectorCompat
and
MotionEventCompat
classes. These classes are in the AndroidX
Library. Use AndroidX classes where possible to provide compatibility with
earlier devices.
MotionEventCompat is not a replacement for the
MotionEvent
class. Rather, it provides static utility methods to which you pass your
MotionEvent object to receive the action associated with that
event.
Gather data
When a user places one or more fingers on the screen, this triggers the
callback
onTouchEvent()
on the view that receives the touch events. For each sequence of touch
events—such as position, pressure, size, and addition of another
finger—that is identified as a gesture, onTouchEvent() is
fired several times.
The gesture starts when the user first touches the screen, continues as the
system tracks the position of the user's finger or fingers, and ends by
capturing the final event of the user's last finger leaving the screen.
Throughout this interaction, the MotionEvent delivered to
onTouchEvent() provides the details of every interaction. Your app
can use the data provided by the MotionEvent to determine whether a
gesture it cares about happens.
Capture touch events for an Activity or View
To intercept touch events in an Activity or
View, override the onTouchEvent() callback.
The following code snippet uses
getAction()
to extract the action the user performs from the event parameter.
This gives you the raw data you need to determine whether a gesture you care
about occurs.
Kotlin
classMainActivity:Activity(){ ... // This example shows an Activity. You can use the same approach if you are // subclassing a View. overridefunonTouchEvent(event:MotionEvent):Boolean{ returnwhen(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
publicclass MainActivityextendsActivity{ ... // This example shows an Activity. You can use the same approach if you are // subclassing a View. @Override publicbooleanonTouchEvent(MotionEventevent){ switch(event.getAction()){ case(MotionEvent.ACTION_DOWN): Log.d(DEBUG_TAG,"Action was DOWN"); returntrue; case(MotionEvent.ACTION_MOVE): Log.d(DEBUG_TAG,"Action was MOVE"); returntrue; case(MotionEvent.ACTION_UP): Log.d(DEBUG_TAG,"Action was UP"); returntrue; case(MotionEvent.ACTION_CANCEL): Log.d(DEBUG_TAG,"Action was CANCEL"); returntrue; case(MotionEvent.ACTION_OUTSIDE): Log.d(DEBUG_TAG,"Movement occurred outside bounds of current screen element"); returntrue; default: returnsuper.onTouchEvent(event); } }
This code produces messages like the following in Logcat as the user taps, touches & holds, and drags:
GESTURESDActionwasDOWN GESTURESDActionwasUP GESTURESDActionwasMOVE
For custom gestures, you can then do your own processing on these events to
determine whether they represent a gesture you need to handle. However, if your
app uses common gestures, such as double-tap, touch & hold, fling, and so on,
you can take advantage of the
GestureDetector
class. GestureDetector makes it easier for you to detect common
gestures without processing the individual touch events yourself. This is
discussed further in Detect gestures.
Capture touch events for a single view
As an alternative to onTouchEvent(), you can attach a
View.OnTouchListener
object to any View
object using the
setOnTouchListener()
method. This makes it possible to listen for touch events without subclassing an
existing View, as shown in the following example:
Kotlin
findViewById<View>(R.id.my_view).setOnTouchListener{v,event-> // Respond to touch events. true }
Java
ViewmyView=findViewById(R.id.my_view); myView.setOnTouchListener(newOnTouchListener(){ publicbooleanonTouch(Viewv,MotionEventevent){ // Respond to touch events. returntrue; } });
Beware of creating a listener that returns false for the
ACTION_DOWN event.
If you do this, the listener isn't called for the subsequent
ACTION_MOVE and
ACTION_UP sequence of
events. This is because ACTION_DOWN is the starting point for all
touch events.
If you are creating a custom view, you can override
onTouchEvent(), as described earlier.
Detect gestures
Android provides the GestureDetector class for detecting common
gestures. Some of the gestures it supports include
onDown() ,
onLongPress() ,
and
onFling() .
You can use GestureDetector in conjunction with the
onTouchEvent() method described earlier.
Detect all supported gestures
When you instantiate a GestureDetectorCompat object, one of the
parameters it takes is a class that implements the
GestureDetector.OnGestureListener
interface. GestureDetector.OnGestureListener notifies users when a
particular touch event occurs. To make it possible for your
GestureDetector object to receive events, override the view or
activity's onTouchEvent() method and pass along all observed events
to the detector instance.
In the following snippet, a return value of true from the
individual on<TouchEvent> methods indicates that the
touch event is handled. A return value of false passes events down
through the view stack until the touch is successfully handled.
If you run the following snippet in a test app, you can get a feel for how
actions are triggered when you interact with the touch screen and what the
contents of the MotionEvent are for each touch event. You then see
how much data is being generated for simple interactions.
Kotlin
privateconstvalDEBUG_TAG="Gestures" classMainActivity: Activity(), GestureDetector.OnGestureListener, GestureDetector.OnDoubleTapListener{ privatelateinitvarmDetector:GestureDetectorCompat // Called when the activity is first created. publicoverridefunonCreate(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) } overridefunonTouchEvent(event:MotionEvent):Boolean{ returnif(mDetector.onTouchEvent(event)){ true }else{ super.onTouchEvent(event) } } overridefunonDown(event:MotionEvent):Boolean{ Log.d(DEBUG_TAG,"onDown: $event") returntrue } overridefunonFling( event1:MotionEvent, event2:MotionEvent, velocityX:Float, velocityY:Float ):Boolean{ Log.d(DEBUG_TAG,"onFling: $event1$event2") returntrue } overridefunonLongPress(event:MotionEvent){ Log.d(DEBUG_TAG,"onLongPress: $event") } overridefunonScroll( event1:MotionEvent, event2:MotionEvent, distanceX:Float, distanceY:Float ):Boolean{ Log.d(DEBUG_TAG,"onScroll: $event1$event2") returntrue } overridefunonShowPress(event:MotionEvent){ Log.d(DEBUG_TAG,"onShowPress: $event") } overridefunonSingleTapUp(event:MotionEvent):Boolean{ Log.d(DEBUG_TAG,"onSingleTapUp: $event") returntrue } overridefunonDoubleTap(event:MotionEvent):Boolean{ Log.d(DEBUG_TAG,"onDoubleTap: $event") returntrue } overridefunonDoubleTapEvent(event:MotionEvent):Boolean{ Log.d(DEBUG_TAG,"onDoubleTapEvent: $event") returntrue } overridefunonSingleTapConfirmed(event:MotionEvent):Boolean{ Log.d(DEBUG_TAG,"onSingleTapConfirmed: $event") returntrue } }
Java
publicclass MainActivityextendsActivityimplements GestureDetector.OnGestureListener, GestureDetector.OnDoubleTapListener{ privatestaticfinalStringDEBUG_TAG="Gestures"; privateGestureDetectorCompatmDetector; // Called when the activity is first created. @Override publicvoidonCreate(BundlesavedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Instantiate the gesture detector with the // application context and an implementation of // GestureDetector.OnGestureListener. mDetector=newGestureDetectorCompat(this,this); // Set the gesture detector as the double-tap // listener. mDetector.setOnDoubleTapListener(this); } @Override publicbooleanonTouchEvent(MotionEventevent){ if(this.mDetector.onTouchEvent(event)){ returntrue; } returnsuper.onTouchEvent(event); } @Override publicbooleanonDown(MotionEventevent){ Log.d(DEBUG_TAG,"onDown: "+event.toString()); returntrue; } @Override publicbooleanonFling(MotionEventevent1,MotionEventevent2, floatvelocityX,floatvelocityY){ Log.d(DEBUG_TAG,"onFling: "+event1.toString()+event2.toString()); returntrue; } @Override publicvoidonLongPress(MotionEventevent){ Log.d(DEBUG_TAG,"onLongPress: "+event.toString()); } @Override publicbooleanonScroll(MotionEventevent1,MotionEventevent2,floatdistanceX, floatdistanceY){ Log.d(DEBUG_TAG,"onScroll: "+event1.toString()+event2.toString()); returntrue; } @Override publicvoidonShowPress(MotionEventevent){ Log.d(DEBUG_TAG,"onShowPress: "+event.toString()); } @Override publicbooleanonSingleTapUp(MotionEventevent){ Log.d(DEBUG_TAG,"onSingleTapUp: "+event.toString()); returntrue; } @Override publicbooleanonDoubleTap(MotionEventevent){ Log.d(DEBUG_TAG,"onDoubleTap: "+event.toString()); returntrue; } @Override publicbooleanonDoubleTapEvent(MotionEventevent){ Log.d(DEBUG_TAG,"onDoubleTapEvent: "+event.toString()); returntrue; } @Override publicbooleanonSingleTapConfirmed(MotionEventevent){ Log.d(DEBUG_TAG,"onSingleTapConfirmed: "+event.toString()); returntrue; } }
Detect a subset of supported gestures
If you only want to process a few gestures, you can extend
GestureDetector.SimpleOnGestureListener
instead of implementing the GestureDetector.OnGestureListener
interface.
GestureDetector.SimpleOnGestureListener provides an
implementation for all of the
on<TouchEvent> methods by returning
false for all of them. This lets you override only the methods you
care about. For example, the following code snippet creates a class that extends
GestureDetector.SimpleOnGestureListener and overrides
onFling() and onDown().
Whether you use GestureDetector.OnGestureListener or
GestureDetector.SimpleOnGestureListener, it's a best practice to
implement an onDown() method that returns true. This
is because all gestures begin with an onDown() message. If you
return false from onDown(), as
GestureDetector.SimpleOnGestureListener does by default, the system
assumes you want to ignore the rest of the gesture, and the other methods of
GestureDetector.OnGestureListener aren't called. This might cause
unexpected problems in your app. Only return false from
onDown() if you truly want to ignore an entire gesture.
Kotlin
privateconstvalDEBUG_TAG="Gestures" classMainActivity:Activity(){ privatelateinitvarmDetector:GestureDetectorCompat publicoverridefunonCreate(savedInstanceState:Bundle?){ super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) mDetector=GestureDetectorCompat(this,MyGestureListener()) } overridefunonTouchEvent(event:MotionEvent):Boolean{ mDetector.onTouchEvent(event) returnsuper.onTouchEvent(event) } privateclassMyGestureListener:GestureDetector.SimpleOnGestureListener(){ overridefunonDown(event:MotionEvent):Boolean{ Log.d(DEBUG_TAG,"onDown: $event") returntrue } overridefunonFling( event1:MotionEvent, event2:MotionEvent, velocityX:Float, velocityY:Float ):Boolean{ Log.d(DEBUG_TAG,"onFling: $event1$event2") returntrue } } }
Java
publicclass MainActivityextendsActivity{ privateGestureDetectorCompatmDetector; @Override publicvoidonCreate(BundlesavedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mDetector=newGestureDetectorCompat(this,newMyGestureListener()); } @Override publicbooleanonTouchEvent(MotionEventevent){ if(this.mDetector.onTouchEvent(event)){ returntrue; } returnsuper.onTouchEvent(event); } class MyGestureListenerextendsGestureDetector.SimpleOnGestureListener{ privatestaticfinalStringDEBUG_TAG="Gestures"; @Override publicbooleanonDown(MotionEventevent){ Log.d(DEBUG_TAG,"onDown: "+event.toString()); returntrue; } @Override publicbooleanonFling(MotionEventevent1,MotionEventevent2, floatvelocityX,floatvelocityY){ Log.d(DEBUG_TAG,"onFling: "+event1.toString()+event2.toString()); returntrue; } } }