| && repo sync -j8 | 518edbf | 2012-11-30 16:28:27 -0800 | [diff] [blame] | 1 | page.title=Detecting Common Gestures |
| 2 | parent.title=Using Touch Gestures |
| 3 | parent.link=index.html |
| 4 | |
| 5 | trainingnavtop=true |
| 6 | next.title=Tracking Movement |
| 7 | next.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 -j8 | 518edbf | 2012-11-30 16:28:27 -0800 | [diff] [blame] | 28 | <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 | |
| kmccormick | e23f97b | 2013-03-12 14:46:07 -0700 | [diff] [blame] | 33 | <h2>Try it out</h2> |
| 34 | |
| 35 | <div class="download-box"> |
| 36 | <a href="{@docRoot}shareables/training/InteractiveChart.zip" |
| 37 | class="button">Download the sample</a> |
| 38 | <p class="filename">InteractiveChart.zip</p> |
| 39 | </div> |
| && repo sync -j8 | 518edbf | 2012-11-30 16:28:27 -0800 | [diff] [blame] | 40 | |
| 41 | </div> |
| 42 | </div> |
| 43 | |
| 44 | <p>A "touch gesture" occurs when a user places one or more fingers on the touch |
| 45 | screen, and your application interprets |
| 46 | that pattern of touches as a particular gesture. There are correspondingly two |
| 47 | phases 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 |
| 53 | gestures 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} |
| 60 | and {@link android.support.v4.view.MotionEventCompat} classes. These classes are in the |
| Scott Main | 4e2c9dc | 2013-07-23 19:35:17 -0700 | [diff] [blame] | 61 | <a href="{@docRoot}tools/support-library/index.html">Support Library</a>. You should use |
| && repo sync -j8 | 518edbf | 2012-11-30 16:28:27 -0800 | [diff] [blame] | 62 | Support Library classes where possible to provide compatibility with devices |
| 63 | running Android 1.6 and higher. Note that {@link android.support.v4.view.MotionEventCompat} is <em>not</em> a |
| 64 | replacement for the {@link android.view.MotionEvent} class. Rather, it provides static utility |
| 65 | methods to which you pass your {@link android.view.MotionEvent} object in order to receive |
| 66 | the 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 |
| 71 | callback {@link android.view.View#onTouchEvent onTouchEvent()} |
| 72 | on the View that received the touch events. |
| 73 | For each sequence of touch events (position, pressure, size, addition of another finger, etc.) |
| 74 | that 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 |
| 78 | the position of the user's finger(s), and ends by capturing the final event of |
| 79 | the user's fingers leaving the screen. Throughout this interaction, |
| 80 | the {@link android.view.MotionEvent} delivered to {@link android.view.View#onTouchEvent onTouchEvent()} |
| 81 | provides the details of every interaction. Your app can use the data provided by the {@link android.view.MotionEvent} |
| 82 | to determine if a gesture it cares |
| 83 | about 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 |
| 88 | the {@link android.view.View#onTouchEvent onTouchEvent()} callback.</p> |
| 89 | |
| 90 | <p>The following snippet uses |
| 91 | {@link android.support.v4.view.MotionEventCompat#getActionMasked getActionMasked()} |
| 92 | to extract the action the user performed from the {@code event} parameter. This gives you the raw |
| 93 | data you need to determine if a gesture you care about occurred:</p> |
| 94 | |
| 95 | <pre> |
| 96 | public 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 | @Override |
| 101 | public 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 |
| 128 | gesture occurred. This is the kind of processing you would have to do for a |
| 129 | custom gesture. However, if your app uses |
| 130 | common gestures such as double tap, long press, fling, and so on, you can |
| 131 | take advantage of the {@link |
| 132 | android.view.GestureDetector} class. {@link |
| 133 | android.view.GestureDetector} makes it easy for you to detect common |
| 134 | gestures without processing the individual touch events yourself. This is |
| 135 | discussed 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()}, |
| 140 | you can attach an {@link android.view.View.OnTouchListener} object to any {@link |
| 141 | android.view.View} object using the {@link android.view.View#setOnTouchListener |
| 142 | setOnTouchListener()} method. This makes it possible to to listen for touch |
| 143 | events without subclassing an existing {@link android.view.View}. For |
| 144 | example:</p> |
| 145 | |
| 146 | <pre>View myView = findViewById(R.id.my_view); |
| 147 | myView.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 |
| 156 | not be called for the subsequent {@link android.view.MotionEvent#ACTION_MOVE} |
| 157 | and {@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()}, |
| 162 | as described above.</p> |
| 163 | |
| 164 | <h2 id="detect">Detect Gestures</h2> |
| 165 | |
| 166 | <p>Android provides the {@link android.view.GestureDetector} class for detecting |
| 167 | common gestures. Some of the gestures it supports include {@link |
| 168 | android.view.GestureDetector.OnGestureListener#onDown onDown()}, {@link |
| 169 | android.view.GestureDetector.OnGestureListener#onLongPress onLongPress()}, |
| 170 | {@link android.view.GestureDetector.OnGestureListener#onFling onFling()}, and so |
| 171 | on. You can use {@link android.view.GestureDetector} in conjunction with the |
| 172 | {@link android.view.View#onTouchEvent onTouchEvent()} |
| 173 | method 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} |
| 179 | object, 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 |
| 182 | a particular touch event has occurred. To make it possible for your |
| 183 | {@link android.view.GestureDetector} object to receive events, you override |
| 184 | the View or Activity's {@link android.view.View#onTouchEvent onTouchEvent()} method, |
| 185 | and 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><TouchEvent></em>} methods indicates that you |
| 190 | have handled the touch event. A return value of {@code false} passes events down |
| 191 | through 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 |
| 194 | you interact with the touch screen, and what the contents of the {@link |
| 195 | android.view.MotionEvent} are for each touch event. You will realize how much |
| 196 | data 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 | @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 | @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 | @Override |
| 227 | public boolean onDown(MotionEvent event) { |
| 228 | Log.d(DEBUG_TAG,"onDown: " + event.toString()); |
| 229 | return true; |
| 230 | } |
| 231 | |
| 232 | @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 | @Override |
| 240 | public void onLongPress(MotionEvent event) { |
| 241 | Log.d(DEBUG_TAG, "onLongPress: " + event.toString()); |
| 242 | } |
| 243 | |
| 244 | @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 | @Override |
| 252 | public void onShowPress(MotionEvent event) { |
| 253 | Log.d(DEBUG_TAG, "onShowPress: " + event.toString()); |
| 254 | } |
| 255 | |
| 256 | @Override |
| 257 | public boolean onSingleTapUp(MotionEvent event) { |
| 258 | Log.d(DEBUG_TAG, "onSingleTapUp: " + event.toString()); |
| 259 | return true; |
| 260 | } |
| 261 | |
| 262 | @Override |
| 263 | public boolean onDoubleTap(MotionEvent event) { |
| 264 | Log.d(DEBUG_TAG, "onDoubleTap: " + event.toString()); |
| 265 | return true; |
| 266 | } |
| 267 | |
| 268 | @Override |
| 269 | public boolean onDoubleTapEvent(MotionEvent event) { |
| 270 | Log.d(DEBUG_TAG, "onDoubleTapEvent: " + event.toString()); |
| 271 | return true; |
| 272 | } |
| 273 | |
| 274 | @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 |
| 284 | android.view.GestureDetector.SimpleOnGestureListener} instead of implementing |
| 285 | the {@link android.view.GestureDetector.OnGestureListener} interface. </p> |
| 286 | <p> |
| 287 | {@link |
| 288 | android.view.GestureDetector.SimpleOnGestureListener} provides an implementation |
| 289 | for all of the {@code on<em><TouchEvent></em>} methods by returning {@code false} |
| 290 | for all of them. Thus you can override only the methods you care about. |
| 291 | For |
| 292 | example, the snippet below creates a class that extends {@link |
| 293 | android.view.GestureDetector.SimpleOnGestureListener} and overrides {@link |
| 294 | android.view.GestureDetector.OnGestureListener#onFling onFling()} and {@link |
| 295 | android.view.GestureDetector.OnGestureListener#onDown onDown()}.</p> |
| 296 | |
| 297 | <p>Whether or not you use {@link android.view.GestureDetector.OnGestureListener}, |
| 298 | it's best practice to implement an |
| 299 | {@link android.view.GestureDetector.OnGestureListener#onDown onDown()} |
| 300 | method 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()}, |
| 303 | as {@link android.view.GestureDetector.SimpleOnGestureListener} does by default, |
| 304 | the 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. |
| 306 | This has the potential to cause unexpected problems in your app. |
| 307 | The only time you should return {@code false} from |
| 308 | {@link android.view.GestureDetector.OnGestureListener#onDown onDown()} |
| 309 | is 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 | @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 | @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 | @Override |
| 332 | public boolean onDown(MotionEvent event) { |
| 333 | Log.d(DEBUG_TAG,"onDown: " + event.toString()); |
| 334 | return true; |
| 335 | } |
| 336 | |
| 337 | @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 | |