blob: a8e0fdbf166a649f8324b49cdd7cd481bf9ce104 [file] [log] [blame]
&& repo sync -j8518edbf2012-11-30 16:28:27 -08001page.title=Detecting Common Gestures
2parent.title=Using Touch Gestures
3parent.link=index.html
4
5trainingnavtop=true
6next.title=Tracking Movement
7next.link=movement.html
8
9@jd:body
10
11<div id="tb-wrapper">
12<div id="tb">
13
14<!-- table of contents -->
15<h2>This lesson teaches you to</h2>
16<ol>
17 <li><a href="#data">Gather Data</a></li>
18 <li><a href="#detect">Detect Gestures</a></li>
19</ol>
20
21<!-- other docs (NOT javadocs) -->
22<h2>You should also read</h2>
23
24<ul>
25 <li><a href="http://developer.android.com/guide/topics/ui/ui-events.html">Input Events</a> API Guide
26 </li>
27 <li><a href="{@docRoot}guide/topics/sensors/sensors_overview.html">Sensors Overview</a></li>
&& repo sync -j8518edbf2012-11-30 16:28:27 -080028 <li><a href="{@docRoot}training/custom-views/making-interactive.html">Making the View Interactive</a> </li>
29 <li>Design Guide for <a href="{@docRoot}design/patterns/gestures.html">Gestures</a></li>
30 <li>Design Guide for <a href="{@docRoot}design/style/touch-feedback.html">Touch Feedback</a></li>
31</ul>
32
kmccormicke23f97b2013-03-12 14:46:07 -070033<h2>Try it out</h2>
34
35<div class="download-box">
36 <a href="{@docRoot}shareables/training/InteractiveChart.zip"
37class="button">Download the sample</a>
38 <p class="filename">InteractiveChart.zip</p>
39</div>
&& repo sync -j8518edbf2012-11-30 16:28:27 -080040
41</div>
42</div>
43
44<p>A "touch gesture" occurs when a user places one or more fingers on the touch
45screen, and your application interprets
46that pattern of touches as a particular gesture. There are correspondingly two
47phases to gesture detection:</p>
48
49<ol>
50 <li>Gathering data about touch events.</li>
51
52 <li>Interpreting the data to see if it meets the criteria for any of the
53gestures your app supports. </li>
54
55</ol>
56
57<h4>Support Library Classes</h4>
58
59<p>The examples in this lesson use the {@link android.support.v4.view.GestureDetectorCompat}
60and {@link android.support.v4.view.MotionEventCompat} classes. These classes are in the
Scott Main4e2c9dc2013-07-23 19:35:17 -070061<a href="{@docRoot}tools/support-library/index.html">Support Library</a>. You should use
&& repo sync -j8518edbf2012-11-30 16:28:27 -080062Support Library classes where possible to provide compatibility with devices
63running Android 1.6 and higher. Note that {@link android.support.v4.view.MotionEventCompat} is <em>not</em> a
64replacement for the {@link android.view.MotionEvent} class. Rather, it provides static utility
65methods to which you pass your {@link android.view.MotionEvent} object in order to receive
66the desired action associated with that event.</p>
67
68<h2 id="data">Gather Data</h2>
69
70<p>When a user places one or more fingers on the screen, this triggers the
71callback {@link android.view.View#onTouchEvent onTouchEvent()}
72on the View that received the touch events.
73For each sequence of touch events (position, pressure, size, addition of another finger, etc.)
74that is ultimately identified as a gesture,
75{@link android.view.View#onTouchEvent onTouchEvent()} is fired several times.</p>
76
77<p>The gesture starts when the user first touches the screen, continues as the system tracks
78the position of the user's finger(s), and ends by capturing the final event of
79the user's fingers leaving the screen. Throughout this interaction,
80the {@link android.view.MotionEvent} delivered to {@link android.view.View#onTouchEvent onTouchEvent()}
81provides the details of every interaction. Your app can use the data provided by the {@link android.view.MotionEvent}
82to determine if a gesture it cares
83about happened.</p>
84
85<h3>Capturing touch events for an Activity or View</h3>
86
87<p><p>To intercept touch events in an Activity or View, override
88the {@link android.view.View#onTouchEvent onTouchEvent()} callback.</p>
89
90<p>The following snippet uses
91{@link android.support.v4.view.MotionEventCompat#getActionMasked getActionMasked()}
92to extract the action the user performed from the {@code event} parameter. This gives you the raw
93data you need to determine if a gesture you care about occurred:</p>
94
95<pre>
96public class MainActivity extends Activity {
97...
98// This example shows an Activity, but you would use the same approach if
99// you were subclassing a View.
100&#64;Override
101public boolean onTouchEvent(MotionEvent event){
102
103 int action = MotionEventCompat.getActionMasked(event);
104
105 switch(action) {
106 case (MotionEvent.ACTION_DOWN) :
107 Log.d(DEBUG_TAG,"Action was DOWN");
108 return true;
109 case (MotionEvent.ACTION_MOVE) :
110 Log.d(DEBUG_TAG,"Action was MOVE");
111 return true;
112 case (MotionEvent.ACTION_UP) :
113 Log.d(DEBUG_TAG,"Action was UP");
114 return true;
115 case (MotionEvent.ACTION_CANCEL) :
116 Log.d(DEBUG_TAG,"Action was CANCEL");
117 return true;
118 case (MotionEvent.ACTION_OUTSIDE) :
119 Log.d(DEBUG_TAG,"Movement occurred outside bounds " +
120 "of current screen element");
121 return true;
122 default :
123 return super.onTouchEvent(event);
124 }
125}</pre>
126
127<p>You can then do your own processing on these events to determine if a
128gesture occurred. This is the kind of processing you would have to do for a
129custom gesture. However, if your app uses
130common gestures such as double tap, long press, fling, and so on, you can
131take advantage of the {@link
132android.view.GestureDetector} class. {@link
133android.view.GestureDetector} makes it easy for you to detect common
134gestures without processing the individual touch events yourself. This is
135discussed below in <a href="#detect">Detect Gestures</a>.</p>
136
137<h3>Capturing touch events for a single view</h3>
138
139<p>As an alternative to {@link android.view.View#onTouchEvent onTouchEvent()},
140you can attach an {@link android.view.View.OnTouchListener} object to any {@link
141android.view.View} object using the {@link android.view.View#setOnTouchListener
142setOnTouchListener()} method. This makes it possible to to listen for touch
143events without subclassing an existing {@link android.view.View}. For
144example:</p>
145
146<pre>View myView = findViewById(R.id.my_view);
147myView.setOnTouchListener(new OnTouchListener() {
148 public boolean onTouch(View v, MotionEvent event) {
149 // ... Respond to touch events
150 return true;
151 }
152});</pre>
153
154<p>Beware of creating a listener that returns {@code false} for the
155{@link android.view.MotionEvent#ACTION_DOWN} event. If you do this, the listener will
156not be called for the subsequent {@link android.view.MotionEvent#ACTION_MOVE}
157and {@link android.view.MotionEvent#ACTION_UP} string of events. This is because
158{@link android.view.MotionEvent#ACTION_DOWN} is the starting point for all touch events.</p>
159
160<p>If you are creating a custom View, you can override
161{@link android.view.View#onTouchEvent onTouchEvent()},
162as described above.</p>
163
164<h2 id="detect">Detect Gestures</h2>
165
166<p>Android provides the {@link android.view.GestureDetector} class for detecting
167common gestures. Some of the gestures it supports include {@link
168android.view.GestureDetector.OnGestureListener#onDown onDown()}, {@link
169android.view.GestureDetector.OnGestureListener#onLongPress onLongPress()},
170{@link android.view.GestureDetector.OnGestureListener#onFling onFling()}, and so
171on. You can use {@link android.view.GestureDetector} in conjunction with the
172{@link android.view.View#onTouchEvent onTouchEvent()}
173method described above.</p>
174
175
176<h3>Detecting All Supported Gestures</h3>
177
178<p>When you instantiate a {@link android.support.v4.view.GestureDetectorCompat}
179object, one of the parameters it takes is a class that implements the
180{@link android.view.GestureDetector.OnGestureListener} interface.
181{@link android.view.GestureDetector.OnGestureListener} notifies users when
182a particular touch event has occurred. To make it possible for your
183{@link android.view.GestureDetector} object to receive events, you override
184the View or Activity's {@link android.view.View#onTouchEvent onTouchEvent()} method,
185and pass along all observed events to the detector instance.</p>
186
187
188<p>In the following snippet, a return value of {@code true} from the individual
189{@code on<em>&lt;TouchEvent&gt;</em>} methods indicates that you
190have handled the touch event. A return value of {@code false} passes events down
191through the view stack until the touch has been successfully handled.</p>
192
193<p>Run the following snippet to get a feel for how actions are triggered when
194you interact with the touch screen, and what the contents of the {@link
195android.view.MotionEvent} are for each touch event. You will realize how much
196data is being generated for even simple interactions.</p>
197
198<pre>public class MainActivity extends Activity implements
199 GestureDetector.OnGestureListener,
200 GestureDetector.OnDoubleTapListener{
201
202 private static final String DEBUG_TAG = "Gestures";
203 private GestureDetectorCompat mDetector;
204
205 // Called when the activity is first created.
206 &#64;Override
207 public void onCreate(Bundle savedInstanceState) {
208 super.onCreate(savedInstanceState);
209 setContentView(R.layout.activity_main);
210 // Instantiate the gesture detector with the
211 // application context and an implementation of
212 // GestureDetector.OnGestureListener
213 mDetector = new GestureDetectorCompat(this,this);
214 // Set the gesture detector as the double tap
215 // listener.
216 mDetector.setOnDoubleTapListener(this);
217 }
218
219 &#64;Override
220 public boolean onTouchEvent(MotionEvent event){
221 this.mDetector.onTouchEvent(event);
222 // Be sure to call the superclass implementation
223 return super.onTouchEvent(event);
224 }
225
226 &#64;Override
227 public boolean onDown(MotionEvent event) {
228 Log.d(DEBUG_TAG,"onDown: " + event.toString());
229 return true;
230 }
231
232 &#64;Override
233 public boolean onFling(MotionEvent event1, MotionEvent event2,
234 float velocityX, float velocityY) {
235 Log.d(DEBUG_TAG, "onFling: " + event1.toString()+event2.toString());
236 return true;
237 }
238
239 &#64;Override
240 public void onLongPress(MotionEvent event) {
241 Log.d(DEBUG_TAG, "onLongPress: " + event.toString());
242 }
243
244 &#64;Override
245 public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
246 float distanceY) {
247 Log.d(DEBUG_TAG, "onScroll: " + e1.toString()+e2.toString());
248 return true;
249 }
250
251 &#64;Override
252 public void onShowPress(MotionEvent event) {
253 Log.d(DEBUG_TAG, "onShowPress: " + event.toString());
254 }
255
256 &#64;Override
257 public boolean onSingleTapUp(MotionEvent event) {
258 Log.d(DEBUG_TAG, "onSingleTapUp: " + event.toString());
259 return true;
260 }
261
262 &#64;Override
263 public boolean onDoubleTap(MotionEvent event) {
264 Log.d(DEBUG_TAG, "onDoubleTap: " + event.toString());
265 return true;
266 }
267
268 &#64;Override
269 public boolean onDoubleTapEvent(MotionEvent event) {
270 Log.d(DEBUG_TAG, "onDoubleTapEvent: " + event.toString());
271 return true;
272 }
273
274 &#64;Override
275 public boolean onSingleTapConfirmed(MotionEvent event) {
276 Log.d(DEBUG_TAG, "onSingleTapConfirmed: " + event.toString());
277 return true;
278 }
279}</pre>
280
281<h3>Detecting a Subset of Supported Gestures</h3>
282
283<p>If you only want to process a few gestures, you can extend {@link
284android.view.GestureDetector.SimpleOnGestureListener} instead of implementing
285the {@link android.view.GestureDetector.OnGestureListener} interface. </p>
286<p>
287{@link
288android.view.GestureDetector.SimpleOnGestureListener} provides an implementation
289for all of the {@code on<em>&lt;TouchEvent&gt;</em>} methods by returning {@code false}
290for all of them. Thus you can override only the methods you care about.
291For
292example, the snippet below creates a class that extends {@link
293android.view.GestureDetector.SimpleOnGestureListener} and overrides {@link
294android.view.GestureDetector.OnGestureListener#onFling onFling()} and {@link
295android.view.GestureDetector.OnGestureListener#onDown onDown()}.</p>
296
297<p>Whether or not you use {@link android.view.GestureDetector.OnGestureListener},
298it's best practice to implement an
299{@link android.view.GestureDetector.OnGestureListener#onDown onDown()}
300method that returns {@code true}. This is because all gestures begin with an
301{@link android.view.GestureDetector.OnGestureListener#onDown onDown()} message. If you return
302{@code false} from {@link android.view.GestureDetector.OnGestureListener#onDown onDown()},
303as {@link android.view.GestureDetector.SimpleOnGestureListener} does by default,
304the system assumes that you want to ignore the rest of the gesture, and the other methods of
305{@link android.view.GestureDetector.OnGestureListener} never get called.
306This has the potential to cause unexpected problems in your app.
307The only time you should return {@code false} from
308{@link android.view.GestureDetector.OnGestureListener#onDown onDown()}
309is if you truly want to ignore an entire gesture. </p>
310
311<pre>public class MainActivity extends Activity {
312
313 private GestureDetectorCompat mDetector;
314
315 &#64;Override
316 public void onCreate(Bundle savedInstanceState) {
317 super.onCreate(savedInstanceState);
318 setContentView(R.layout.activity_main);
319 mDetector = new GestureDetectorCompat(this, new MyGestureListener());
320 }
321
322 &#64;Override
323 public boolean onTouchEvent(MotionEvent event){
324 this.mDetector.onTouchEvent(event);
325 return super.onTouchEvent(event);
326 }
327
328 class MyGestureListener extends GestureDetector.SimpleOnGestureListener {
329 private static final String DEBUG_TAG = "Gestures";
330
331 &#64;Override
332 public boolean onDown(MotionEvent event) {
333 Log.d(DEBUG_TAG,"onDown: " + event.toString());
334 return true;
335 }
336
337 &#64;Override
338 public boolean onFling(MotionEvent event1, MotionEvent event2,
339 float velocityX, float velocityY) {
340 Log.d(DEBUG_TAG, "onFling: " + event1.toString()+event2.toString());
341 return true;
342 }
343 }
344}
345</pre>
346
347