348 lines
14 KiB
Plaintext
348 lines
14 KiB
Plaintext
page.title=Detecting Common Gestures
|
|
parent.title=Using Touch Gestures
|
|
parent.link=index.html
|
|
|
|
trainingnavtop=true
|
|
next.title=Tracking Movement
|
|
next.link=movement.html
|
|
|
|
@jd:body
|
|
|
|
<div id="tb-wrapper">
|
|
<div id="tb">
|
|
|
|
<!-- table of contents -->
|
|
<h2>This lesson teaches you to</h2>
|
|
<ol>
|
|
<li><a href="#data">Gather Data</a></li>
|
|
<li><a href="#detect">Detect Gestures</a></li>
|
|
</ol>
|
|
|
|
<!-- other docs (NOT javadocs) -->
|
|
<h2>You should also read</h2>
|
|
|
|
<ul>
|
|
<li><a href="http://developer.android.com/guide/topics/ui/ui-events.html">Input Events</a> API Guide
|
|
</li>
|
|
<li><a href="{@docRoot}guide/topics/sensors/sensors_overview.html">Sensors Overview</a></li>
|
|
<li><a href="{@docRoot}training/custom-views/making-interactive.html">Making the View Interactive</a> </li>
|
|
<li>Design Guide for <a href="{@docRoot}design/patterns/gestures.html">Gestures</a></li>
|
|
<li>Design Guide for <a href="{@docRoot}design/style/touch-feedback.html">Touch Feedback</a></li>
|
|
</ul>
|
|
|
|
<h2>Try it out</h2>
|
|
|
|
<div class="download-box">
|
|
<a href="{@docRoot}shareables/training/InteractiveChart.zip"
|
|
class="button">Download the sample</a>
|
|
<p class="filename">InteractiveChart.zip</p>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<p>A "touch gesture" occurs when a user places one or more fingers on the touch
|
|
screen, and your application interprets
|
|
that pattern of touches as a particular gesture. There are correspondingly two
|
|
phases to gesture detection:</p>
|
|
|
|
<ol>
|
|
<li>Gathering data about touch events.</li>
|
|
|
|
<li>Interpreting the data to see if it meets the criteria for any of the
|
|
gestures your app supports. </li>
|
|
|
|
</ol>
|
|
|
|
<h4>Support Library Classes</h4>
|
|
|
|
<p>The examples in this lesson use the {@link android.support.v4.view.GestureDetectorCompat}
|
|
and {@link android.support.v4.view.MotionEventCompat} classes. These classes are in the
|
|
<a href="{@docRoot}tools/support-library/index.html">Support Library</a>. You should use
|
|
Support Library classes where possible to provide compatibility with devices
|
|
running Android 1.6 and higher. Note that {@link android.support.v4.view.MotionEventCompat} is <em>not</em> a
|
|
replacement for the {@link android.view.MotionEvent} class. Rather, it provides static utility
|
|
methods to which you pass your {@link android.view.MotionEvent} object in order to receive
|
|
the desired action associated with that event.</p>
|
|
|
|
<h2 id="data">Gather Data</h2>
|
|
|
|
<p>When a user places one or more fingers on the screen, this triggers the
|
|
callback {@link android.view.View#onTouchEvent onTouchEvent()}
|
|
on the View that received the touch events.
|
|
For each sequence of touch events (position, pressure, size, addition of another finger, etc.)
|
|
that is ultimately identified as a gesture,
|
|
{@link android.view.View#onTouchEvent onTouchEvent()} is fired several times.</p>
|
|
|
|
<p>The gesture starts when the user first touches the screen, continues as the system tracks
|
|
the position of the user's finger(s), and ends by capturing the final event of
|
|
the user's fingers leaving the screen. Throughout this interaction,
|
|
the {@link android.view.MotionEvent} delivered to {@link android.view.View#onTouchEvent onTouchEvent()}
|
|
provides the details of every interaction. Your app can use the data provided by the {@link android.view.MotionEvent}
|
|
to determine if a gesture it cares
|
|
about happened.</p>
|
|
|
|
<h3>Capturing touch events for an Activity or View</h3>
|
|
|
|
<p><p>To intercept touch events in an Activity or View, override
|
|
the {@link android.view.View#onTouchEvent onTouchEvent()} callback.</p>
|
|
|
|
<p>The following snippet uses
|
|
{@link android.support.v4.view.MotionEventCompat#getActionMasked getActionMasked()}
|
|
to extract the action the user performed from the {@code event} parameter. This gives you the raw
|
|
data you need to determine if a gesture you care about occurred:</p>
|
|
|
|
<pre>
|
|
public class MainActivity extends Activity {
|
|
...
|
|
// This example shows an Activity, but you would use the same approach if
|
|
// you were subclassing a View.
|
|
@Override
|
|
public boolean onTouchEvent(MotionEvent event){
|
|
|
|
int action = MotionEventCompat.getActionMasked(event);
|
|
|
|
switch(action) {
|
|
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);
|
|
}
|
|
}</pre>
|
|
|
|
<p>You can then do your own processing on these events to determine if a
|
|
gesture occurred. This is the kind of processing you would have to do for a
|
|
custom gesture. However, if your app uses
|
|
common gestures such as double tap, long press, fling, and so on, you can
|
|
take advantage of the {@link
|
|
android.view.GestureDetector} class. {@link
|
|
android.view.GestureDetector} makes it easy for you to detect common
|
|
gestures without processing the individual touch events yourself. This is
|
|
discussed below in <a href="#detect">Detect Gestures</a>.</p>
|
|
|
|
<h3>Capturing touch events for a single view</h3>
|
|
|
|
<p>As an alternative to {@link android.view.View#onTouchEvent onTouchEvent()},
|
|
you can attach an {@link android.view.View.OnTouchListener} object to any {@link
|
|
android.view.View} object using the {@link android.view.View#setOnTouchListener
|
|
setOnTouchListener()} method. This makes it possible to to listen for touch
|
|
events without subclassing an existing {@link android.view.View}. For
|
|
example:</p>
|
|
|
|
<pre>View myView = findViewById(R.id.my_view);
|
|
myView.setOnTouchListener(new OnTouchListener() {
|
|
public boolean onTouch(View v, MotionEvent event) {
|
|
// ... Respond to touch events
|
|
return true;
|
|
}
|
|
});</pre>
|
|
|
|
<p>Beware of creating a listener that returns {@code false} for the
|
|
{@link android.view.MotionEvent#ACTION_DOWN} event. If you do this, the listener will
|
|
not be called for the subsequent {@link android.view.MotionEvent#ACTION_MOVE}
|
|
and {@link android.view.MotionEvent#ACTION_UP} string of events. This is because
|
|
{@link android.view.MotionEvent#ACTION_DOWN} is the starting point for all touch events.</p>
|
|
|
|
<p>If you are creating a custom View, you can override
|
|
{@link android.view.View#onTouchEvent onTouchEvent()},
|
|
as described above.</p>
|
|
|
|
<h2 id="detect">Detect Gestures</h2>
|
|
|
|
<p>Android provides the {@link android.view.GestureDetector} class for detecting
|
|
common gestures. Some of the gestures it supports include {@link
|
|
android.view.GestureDetector.OnGestureListener#onDown onDown()}, {@link
|
|
android.view.GestureDetector.OnGestureListener#onLongPress onLongPress()},
|
|
{@link android.view.GestureDetector.OnGestureListener#onFling onFling()}, and so
|
|
on. You can use {@link android.view.GestureDetector} in conjunction with the
|
|
{@link android.view.View#onTouchEvent onTouchEvent()}
|
|
method described above.</p>
|
|
|
|
|
|
<h3>Detecting All Supported Gestures</h3>
|
|
|
|
<p>When you instantiate a {@link android.support.v4.view.GestureDetectorCompat}
|
|
object, one of the parameters it takes is a class that implements the
|
|
{@link android.view.GestureDetector.OnGestureListener} interface.
|
|
{@link android.view.GestureDetector.OnGestureListener} notifies users when
|
|
a particular touch event has occurred. To make it possible for your
|
|
{@link android.view.GestureDetector} object to receive events, you override
|
|
the View or Activity's {@link android.view.View#onTouchEvent onTouchEvent()} method,
|
|
and pass along all observed events to the detector instance.</p>
|
|
|
|
|
|
<p>In the following snippet, a return value of {@code true} from the individual
|
|
{@code on<em><TouchEvent></em>} methods indicates that you
|
|
have handled the touch event. A return value of {@code false} passes events down
|
|
through the view stack until the touch has been successfully handled.</p>
|
|
|
|
<p>Run the following snippet to get a feel for how actions are triggered when
|
|
you interact with the touch screen, and what the contents of the {@link
|
|
android.view.MotionEvent} are for each touch event. You will realize how much
|
|
data is being generated for even simple interactions.</p>
|
|
|
|
<pre>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){
|
|
this.mDetector.onTouchEvent(event);
|
|
// Be sure to call the superclass implementation
|
|
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 e1, MotionEvent e2, float distanceX,
|
|
float distanceY) {
|
|
Log.d(DEBUG_TAG, "onScroll: " + e1.toString()+e2.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;
|
|
}
|
|
}</pre>
|
|
|
|
<h3>Detecting a Subset of Supported Gestures</h3>
|
|
|
|
<p>If you only want to process a few gestures, you can extend {@link
|
|
android.view.GestureDetector.SimpleOnGestureListener} instead of implementing
|
|
the {@link android.view.GestureDetector.OnGestureListener} interface. </p>
|
|
<p>
|
|
{@link
|
|
android.view.GestureDetector.SimpleOnGestureListener} provides an implementation
|
|
for all of the {@code on<em><TouchEvent></em>} methods by returning {@code false}
|
|
for all of them. Thus you can override only the methods you care about.
|
|
For
|
|
example, the snippet below creates a class that extends {@link
|
|
android.view.GestureDetector.SimpleOnGestureListener} and overrides {@link
|
|
android.view.GestureDetector.OnGestureListener#onFling onFling()} and {@link
|
|
android.view.GestureDetector.OnGestureListener#onDown onDown()}.</p>
|
|
|
|
<p>Whether or not you use {@link android.view.GestureDetector.OnGestureListener},
|
|
it's best practice to implement an
|
|
{@link android.view.GestureDetector.OnGestureListener#onDown onDown()}
|
|
method that returns {@code true}. This is because all gestures begin with an
|
|
{@link android.view.GestureDetector.OnGestureListener#onDown onDown()} message. If you return
|
|
{@code false} from {@link android.view.GestureDetector.OnGestureListener#onDown onDown()},
|
|
as {@link android.view.GestureDetector.SimpleOnGestureListener} does by default,
|
|
the system assumes that you want to ignore the rest of the gesture, and the other methods of
|
|
{@link android.view.GestureDetector.OnGestureListener} never get called.
|
|
This has the potential to cause unexpected problems in your app.
|
|
The only time you should return {@code false} from
|
|
{@link android.view.GestureDetector.OnGestureListener#onDown onDown()}
|
|
is if you truly want to ignore an entire gesture. </p>
|
|
|
|
<pre>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){
|
|
this.mDetector.onTouchEvent(event);
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
</pre>
|
|
|
|
|