From 518edbfa97dbdc366f1e03c62ae275c388ec20ef Mon Sep 17 00:00:00 2001
From: && repo sync -j8 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: 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
+Support Library. 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 not 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. 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. 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. To intercept touch events in an Activity or View, override
+the {@link android.view.View#onTouchEvent onTouchEvent()} callback. 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: 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 Detect Gestures. 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: 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. If you are creating a custom View, you can override
+{@link android.view.View#onTouchEvent onTouchEvent()},
+as described above. 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. 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. In the following snippet, a return value of {@code true} from the individual
+{@code on<TouchEvent>} 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. 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. 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.
+{@link
+android.view.GestureDetector.SimpleOnGestureListener} provides an implementation
+for all of the {@code on<TouchEvent>} 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()}. 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. This class describes how to write apps that allow users to interact with an
+app via touch gestures. Android provides a variety of APIs to
+help you create and detect gestures. Although your app should not depend on touch gestures for basic behaviors (since the gestures
+may not be available to all users in all contexts), adding touch-based
+interaction to your app can greatly increase its usefulness and appeal. To
+provide users with a consistent, intuitive experience, your app should follow
+the accepted Android conventions for touch gestures. The Gestures
+design guide
+shows you how to use common gestures in Android apps. Also see the Design Guide
+for Touch Feedback. This lesson describes how to track movement in touch events. A new {@link
+android.view.View#onTouchEvent onTouchEvent()} is triggered with an {@link
+android.view.MotionEvent#ACTION_MOVE} event whenever the current touch contact
+position, pressure, or size changes. As described in Detecting Common Gestures, all of these events are
+recorded in the {@link android.view.MotionEvent} parameter of {@link
+android.view.View#onTouchEvent onTouchEvent()}. Because finger-based touch isn't always the most precise form of interaction,
+detecting touch events is often based more on movement than on simple contact.
+To help apps distinguish between movement-based gestures (such as a swipe) and
+non-movement gestures (such as a single tap), Android includes the notion of
+"touch slop." Touch slop refers to the distance in pixels a user's touch can wander
+before the gesture is interpreted as a movement-based gesture. For more discussion of this
+topic, see Managing Touch Events in a ViewGroup. There are several different ways to track movement in a gesture, depending on
+the needs of your application. For example: You could have a movement-based gesture that is simply based on the distance and/or direction the pointer traveled. But velocity often is a
+determining factor in tracking a gesture's characteristics or even deciding
+whether the gesture occurred. To make velocity calculation easier, Android
+provides the {@link android.view.VelocityTracker} class and the
+{@link android.support.v4.view.VelocityTrackerCompat} class in the
+Support Library.
+{@link
+android.view.VelocityTracker} helps you track the velocity of touch events. This
+is useful for gestures in which velocity is part of the criteria for the
+gesture, such as a fling. Here is a simple example that illustrates the purpose of the methods in the
+{@link android.view.VelocityTracker} API: Note: Note that you should calculate velocity after an
+{@link android.view.MotionEvent#ACTION_MOVE} event,
+not after {@link android.view.MotionEvent#ACTION_UP}. After an {@link android.view.MotionEvent#ACTION_UP},
+the X and Y velocities will be 0.
+ A multi-touch gesture is when multiple pointers (fingers) touch the screen
+at the same time. This lesson describes how to detect gestures that involve
+multiple pointers. When multiple pointers touch the screen at the same time, the system generates the
+following touch events: You keep track of individual pointers within a {@link
+android.view.MotionEvent} via each pointer's index and ID: The order in which individual pointers appear within a motion event is
+undefined. Thus the index of a pointer can change from one event to the
+next, but the pointer ID of a pointer is guaranteed to remain constant as long
+as the pointer remains active. Use the {@link
+android.view.MotionEvent#getPointerId getPointerId()} method to obtain a
+pointer's ID to track the pointer across all subsequent motion events in a
+gesture. Then for successive motion events, use the {@link
+android.view.MotionEvent#findPointerIndex findPointerIndex()} method to obtain
+the pointer index for a given pointer ID in that motion event. For example: You should always use the method
+{@link android.view.MotionEvent#getActionMasked getActionMasked()} (or better yet, the compatability version
+{@link android.support.v4.view.MotionEventCompat#getActionMasked MotionEventCompat.getActionMasked()}) to retrieve
+the action of a
+{@link android.view.MotionEvent}. Unlike the older {@link android.view.MotionEvent#getAction getAction()}
+method, {@link android.support.v4.view.MotionEventCompat#getActionMasked getActionMasked()} is designed to work with
+multiple pointers. It returns the masked action
+being performed, without including the pointer index bits. You can then use
+{@link android.support.v4.view.MotionEventCompat#getActionIndex getActionIndex()} to return the index of
+the pointer associated with the action. This is illustrated in the snippet below. Note: This example uses the
+{@link android.support.v4.view.MotionEventCompat}
+class. This class is in the
+Support Library. You should use
+{@link android.support.v4.view.MotionEventCompat} to provide the best support for a wide range of
+platforms. Note that {@link android.support.v4.view.MotionEventCompat} is not 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. For more discussion of multi-touch and some examples, see the lesson Dragging and Scaling.
diff --git a/docs/html/training/gestures/scale.jd b/docs/html/training/gestures/scale.jd
new file mode 100644
index 0000000..17e4085
--- /dev/null
+++ b/docs/html/training/gestures/scale.jd
@@ -0,0 +1,240 @@
+page.title=Dragging and Scaling
+parent.title=Using Touch Gestures
+parent.link=index.html
+
+trainingnavtop=true
+next.title=Managing Touch Events in a ViewGroup
+next.link=viewgroup.html
+
+@jd:body
+
+ This lesson describes how to use touch gestures to drag and scale on-screen
+objects, using {@link android.view.View#onTouchEvent onTouchEvent()} to intercept
+touch events. Here is the original source code
+for the examples used in this lesson.
+ If you are targeting Android 3.0 or higher, you can use the built-in drag-and-drop event
+listeners with {@link android.view.View.OnDragListener}, as described in
+Drag and Drop.
+
+ A common operation for a touch gesture is to use it to drag an object across
+the screen. The following snippet lets the user drag an on-screen image. Note
+the following: The following snippet enables a user to drag an object around on the screen. It records the initial
+position of the active pointer, calculates the distance the pointer traveled, and moves the object to the
+new position. It correctly manages the possibility of additional pointers, as described
+above. Notice that the snippet uses the {@link android.view.MotionEvent#getActionMasked getActionMasked()} method.
+You should always use this method (or better yet, the compatability version
+{@link android.support.v4.view.MotionEventCompat#getActionMasked MotionEventCompat.getActionMasked()})
+to retrieve the action of a
+{@link android.view.MotionEvent}. Unlike the older
+{@link android.view.MotionEvent#getAction getAction()}
+method, {@link android.support.v4.view.MotionEventCompat#getActionMasked getActionMasked()}
+is designed to work with multiple pointers. It returns the masked action
+being performed, without including the pointer index bits. As discussed in Detecting Common Gestures,
+{@link android.view.GestureDetector} helps you detect common gestures used by
+Android such as scrolling, flinging, and long press. For scaling, Android
+provides {@link android.view.ScaleGestureDetector}. {@link
+android.view.GestureDetector} and {@link android.view.ScaleGestureDetector} can
+be used together when you want a view to recognize additional gestures. To report detected gesture events, gesture detectors use listener objects
+passed to their constructors. {@link android.view.ScaleGestureDetector} uses
+{@link android.view.ScaleGestureDetector.OnScaleGestureListener}.
+Android provides
+{@link android.view.ScaleGestureDetector.SimpleOnScaleGestureListener}
+as a helper class that you can extend if you don’t care about all of the reported events. Here is a snippet that gives you the basic idea of how to perform scaling.
+Here is the original source code
+for the examples. In Android, scrolling is typically achieved by using the
+{@link android.widget.ScrollView}
+class. Any standard layout that might extend beyond the bounds of its container should be
+nested in a {@link android.widget.ScrollView} to provide a scrollable view that's
+managed by the framework. Implementing a custom scroller should only be
+necessary for special scenarios. This lesson describes such a scenario: displaying
+a scrolling effect in response to touch gestures using scrollers.
+
+
+ You can use scrollers ({@link android.widget.Scroller} or {@link
+android.widget.OverScroller}) to collect the data you need to produce a
+scrolling animation in response to a touch event. {@link
+android.widget.Scroller} and {@link android.widget.OverScroller} are largely
+interchangeable—the difference is that {@link android.widget.OverScroller}
+allows temporarily scrolling beyond the minimum/maximum boundaries and springing
+back to the bounds. This is normally rendered using a "glow" effect, provided by
+the {@link android.widget.EdgeEffect} or {@link
+android.support.v4.widget.EdgeEffectCompat} classes. A scroller is used to animate scrolling over time, using platform-standard
+scrolling physics (friction, velocity, etc.). The scroller itself doesn't
+actually draw anything. Scrollers track scroll offsets for you over time, but
+they don't automatically apply those positions to your view. It's your
+responsibility to get and apply new coordinates at a rate that will make the
+scrolling animation look smooth. Note: You generally only need to use scrollers
+when implementing scrolling yourself. {@link android.widget.ScrollView} and
+{@link android.widget.HorizontalScrollView} do all this for you do all of this for you if you nest your layout within them. This snippet illustrates the basics of using a scroller. It uses a
+{@link android.view.GestureDetector}, and overrides the
+{@link android.view.GestureDetector.SimpleOnGestureListener} methods
+{@link android.view.GestureDetector.OnGestureListener#onDown onDown()} and
+{@link android.view.GestureDetector.OnGestureListener#onFling onFling()}. It also
+overrides {@link android.view.GestureDetector.OnGestureListener#onScroll onScroll()}
+to return {@code false} since you don't need to animate a scroll. It's common to use scrollers in conjunction with a fling gesture, but they
+can be used in pretty much any context where you want the UI to display
+scrolling in response to a touch event. For example, you could override {@link
+android.view.View#onTouchEvent onTouchEvent()} to process touch events directly,
+and produce a scrolling effect in response to those touch events. For another example of scroller usage, see the source code for the
+{@link android.support.v4.view.ViewPager} class. It scrolls in response to flings,
+and uses scrolling to implement the "snapping to page" animation. Handling touch events in a {@link android.view.ViewGroup} takes special care,
+because it's common for a {@link android.view.ViewGroup} to have children that
+are targets for different touch events than the {@link android.view.ViewGroup}
+itself. To make sure that each view correctly receives the touch events intended
+for it, override the {@link android.view.ViewGroup#onInterceptTouchEvent
+onInterceptTouchEvent()} method. The {@link android.view.ViewGroup#onInterceptTouchEvent onInterceptTouchEvent()}
+method is called whenever a touch event is detected on the surface of a
+{@link android.view.ViewGroup}, including on the surface of its children. If
+{@link android.view.ViewGroup#onInterceptTouchEvent onInterceptTouchEvent()}
+returns {@code true}, the {@link android.view.MotionEvent} is intercepted,
+meaning it will be not be passed on to the child, but rather to the
+{@link android.view.View#onTouchEvent onTouchEvent()} method of the parent. The {@link android.view.ViewGroup#onInterceptTouchEvent onInterceptTouchEvent()}
+method gives a parent the chance to see any touch event before its children do.
+If you return {@code true} from
+{@link android.view.ViewGroup#onInterceptTouchEvent onInterceptTouchEvent()},
+the child view that was previously handling touch events
+receives an {@link android.view.MotionEvent#ACTION_CANCEL}, and the events from that
+point forward are sent to the parent's
+{@link android.view.View#onTouchEvent onTouchEvent()} method for the usual handling.
+{@link android.view.ViewGroup#onInterceptTouchEvent onInterceptTouchEvent()} can also
+return {@code false} and simply spy on events as they travel down the view hierarchy
+to their usual targets, which will handle the events with their own
+{@link android.view.View#onTouchEvent onTouchEvent()}.
+
+
+ In the following snippet, the class {@code MyViewGroup} extends
+{@link android.view.ViewGroup}.
+{@code MyViewGroup} contains multiple child views. If you drag your finger across
+a child view horizontally, the child view should no longer get touch events, and
+{@code MyViewGroup} should handle touch events by scrolling its contents. However,
+if you press buttons in the child view, or scroll the child view vertically,
+the parent shouldn't intercept those touch events, because the child is the
+intended target. In those cases,
+{@link android.view.ViewGroup#onInterceptTouchEvent onInterceptTouchEvent()} should
+return {@code false}, and {@code MyViewGroup}'s
+{@link android.view.View#onTouchEvent onTouchEvent()} won't be called. Note that {@link android.view.ViewGroup} also provides a
+{@link android.view.ViewGroup#requestDisallowInterceptTouchEvent requestDisallowInterceptTouchEvent()} method.
+The {@link android.view.ViewGroup} calls this method when a child does not want the parent and its
+ancestors to intercept touch events with
+{@link android.view.ViewGroup#onInterceptTouchEvent onInterceptTouchEvent()}.
+ The above snippet uses the current {@link android.view.ViewConfiguration} to initialize
+a variable called {@code mTouchSlop}. You can use the {@link
+android.view.ViewConfiguration} class to access common distances, speeds, and
+times used by the Android system. "Touch slop" refers to the distance in pixels a user's touch can wander
+before the gesture is interpreted as scrolling. Touch slop is typically used to
+prevent accidental scrolling when the user is performing some other touch
+operation, such as touching on-screen elements. Two other commonly used {@link android.view.ViewConfiguration} methods are
+{@link android.view.ViewConfiguration#getScaledMinimumFlingVelocity getScaledMinimumFlingVelocity()}
+and {@link android.view.ViewConfiguration#getScaledMaximumFlingVelocity getScaledMaximumFlingVelocity()}.
+These methods return the minimum and maximum velocity (respectively) to initiate a fling,
+as measured in pixels per second. For example: Android provides the {@link android.view.TouchDelegate} class to make it possible
+for a parent to extend the touchable area of a child view beyond the child's bounds.
+
+This is useful when the child has to be small, but should have a larger touch region. You can
+also use this approach to shrink the child's touch region if need be. In the following example, an {@link android.widget.ImageButton} is the
+"delegate view" (that is, the child whose touch area the parent will extend).
+Here is the layout file: The snippet below does the following:This lesson teaches you to
+
+
+
+
+You should also read
+
+
+
+
+
+
+
+
+Support Library Classes
+
+Gather Data
+
+Capturing touch events for an Activity or View
+
+
+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);
+ }
+}
+
+Capturing touch events for a single view
+
+View myView = findViewById(R.id.my_view);
+myView.setOnTouchListener(new OnTouchListener() {
+ public boolean onTouch(View v, MotionEvent event) {
+ // ... Respond to touch events
+ return true;
+ }
+});
+
+Detect Gestures
+
+Detecting All Supported Gestures
+
+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;
+ }
+}
+
+Detecting a Subset of Supported Gestures
+
+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;
+ }
+ }
+}
+
+
+
diff --git a/docs/html/training/gestures/index.jd b/docs/html/training/gestures/index.jd
new file mode 100644
index 0000000..0191450
--- /dev/null
+++ b/docs/html/training/gestures/index.jd
@@ -0,0 +1,94 @@
+page.title=Using Touch Gestures
+trainingnavtop=true
+startpage=true
+next.title=Detect Built-in Gestures
+next.link=detector.html
+
+
+@jd:body
+Dependencies and prerequisites
+
+
+
+You should also read
+
+
+
+
+Lessons
+
+
+
diff --git a/docs/html/training/gestures/movement.jd b/docs/html/training/gestures/movement.jd
new file mode 100644
index 0000000..f2c49d7
--- /dev/null
+++ b/docs/html/training/gestures/movement.jd
@@ -0,0 +1,151 @@
+page.title=Tracking Movement
+parent.title=Using Touch Gestures
+parent.link=index.html
+
+trainingnavtop=true
+next.title=Animating a Scroll Gesture
+next.link=scroll.html
+
+@jd:body
+
+This lesson teaches you to
+
+
+
+
+You should also read
+
+
+
+
+
+
+
+
+
+
+
+Track Velocity
+
+public class MainActivity extends Activity {
+ private static final String DEBUG_TAG = "Velocity";
+ ...
+ private VelocityTracker mVelocityTracker = null;
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ int index = event.getActionIndex();
+ int action = event.getActionMasked();
+ int pointerId = event.getPointerId(index);
+
+ switch(action) {
+ case MotionEvent.ACTION_DOWN:
+ if(mVelocityTracker == null) {
+ // Retrieve a new VelocityTracker object to watch the velocity of a motion.
+ mVelocityTracker = VelocityTracker.obtain();
+ }
+ else {
+ // Reset the velocity tracker back to its initial state.
+ mVelocityTracker.clear();
+ }
+ // Add a user's movement to the tracker.
+ mVelocityTracker.addMovement(event);
+ break;
+ case MotionEvent.ACTION_MOVE:
+ mVelocityTracker.addMovement(event);
+ // When you want to determine the velocity, call
+ // computeCurrentVelocity(). Then call getXVelocity()
+ // and getYVelocity() to retrieve the velocity for each pointer ID.
+ mVelocityTracker.computeCurrentVelocity(1000);
+ // Log velocity of pixels per second
+ // Best practice to use VelocityTrackerCompat where possible.
+ Log.d("", "X velocity: " +
+ VelocityTrackerCompat.getXVelocity(mVelocityTracker,
+ pointerId));
+ Log.d("", "Y velocity: " +
+ VelocityTrackerCompat.getYVelocity(mVelocityTracker,
+ pointerId));
+ break;
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ // Return a VelocityTracker object back to be re-used by others.
+ mVelocityTracker.recycle();
+ break;
+ }
+ return true;
+ }
+}
+
+
+This lesson teaches you to
+
+
+
+You should also read
+
+
+
+
+
+Track Multiple Pointers
+
+
+
+
+
+
+
+private int mActivePointerId;
+
+public boolean onTouchEvent(MotionEvent event) {
+ ....
+ // Get the pointer ID
+ mActivePointerId = event.getPointerId(0);
+
+ // ... Many touch events later...
+
+ // Use the pointer ID to find the index of the active pointer
+ // and fetch its position
+ int pointerIndex = event.findPointerIndex(mActivePointerId);
+ // Get the pointer's current position
+ float x = event.getX(pointerIndex);
+ float y = event.getY(pointerIndex);
+}
+
+Get a MotionEvent's Action
+
+int action = MotionEventCompat.getActionMasked(event);
+// Get the index of the pointer associated with the action.
+int index = MotionEventCompat.getActionIndex(event);
+int xPos = -1;
+int yPos = -1;
+
+Log.d(DEBUG_TAG,"The action is " + actionToString(action));
+
+if (event.getPointerCount() > 1) {
+ Log.d(DEBUG_TAG,"Multitouch event");
+ // The coordinates of the current screen contact, relative to
+ // the responding View or Activity.
+ xPos = (int)MotionEventCompat.getX(event, index);
+ yPos = (int)MotionEventCompat.getY(event, index);
+
+} else {
+ // Single touch event
+ Log.d(DEBUG_TAG,"Single touch event");
+ xPos = (int)MotionEventCompat.getX(event, index);
+ yPos = (int)MotionEventCompat.getY(event, index);
+}
+...
+
+// Given an action int, returns a string description
+public static String actionToString(int action) {
+ switch (action) {
+
+ case MotionEvent.ACTION_DOWN: return "Down";
+ case MotionEvent.ACTION_MOVE: return "Move";
+ case MotionEvent.ACTION_POINTER_DOWN: return "Pointer Down";
+ case MotionEvent.ACTION_UP: return "Up";
+ case MotionEvent.ACTION_POINTER_UP: return "Pointer Up";
+ case MotionEvent.ACTION_OUTSIDE: return "Outside";
+ case MotionEvent.ACTION_CANCEL: return "Cancel";
+ }
+ return "";
+}
+
+
+
+
+This lesson teaches you to
+
+
+
+You should also read
+
+
+
+
+
+Drag an Object
+
+
+
+
+
+// The ‘active pointer’ is the one currently moving our object.
+private int mActivePointerId = INVALID_POINTER_ID;
+
+@Override
+public boolean onTouchEvent(MotionEvent ev) {
+ // Let the ScaleGestureDetector inspect all events.
+ mScaleDetector.onTouchEvent(ev);
+
+ final int action = MotionEventCompat.getActionMasked(ev);
+
+ switch (action) {
+ case MotionEvent.ACTION_DOWN: {
+ final int pointerIndex = MotionEventCompat.getActionIndex(ev);
+ final float x = MotionEventCompat.getX(ev, pointerIndex);
+ final float y = MotionEventCompat.getY(ev, pointerIndex);
+
+ // Remember where we started (for dragging)
+ mLastTouchX = x;
+ mLastTouchY = y;
+ // Save the ID of this pointer (for dragging)
+ mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
+ break;
+ }
+
+ case MotionEvent.ACTION_MOVE: {
+ // Find the index of the active pointer and fetch its position
+ final int pointerIndex =
+ MotionEventCompat.findPointerIndex(ev, mActivePointerId);
+
+ final float x = MotionEventCompat.getX(ev, pointerIndex);
+ final float y = MotionEventCompat.getY(ev, pointerIndex);
+
+ // Only move if the ScaleGestureDetector isn't processing a gesture.
+ if (!mScaleDetector.isInProgress()) {
+ // Calculate the distance moved
+ final float dx = x - mLastTouchX;
+ final float dy = y - mLastTouchY;
+
+ mPosX += dx;
+ mPosY += dy;
+
+ invalidate();
+ }
+ // Remember this touch position for the next move event
+ mLastTouchX = x;
+ mLastTouchY = y;
+
+ break;
+ }
+
+ case MotionEvent.ACTION_UP: {
+ mActivePointerId = INVALID_POINTER_ID;
+ break;
+ }
+
+ case MotionEvent.ACTION_CANCEL: {
+ mActivePointerId = INVALID_POINTER_ID;
+ break;
+ }
+
+ case MotionEvent.ACTION_POINTER_UP: {
+
+ final int pointerIndex = MotionEventCompat.getActionIndex(ev);
+ final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex);
+
+ if (pointerId == mActivePointerId) {
+ // This was our active pointer going up. Choose a new
+ // active pointer and adjust accordingly.
+ final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
+ mLastTouchX = MotionEventCompat.getX(ev, newPointerIndex);
+ mLastTouchY = MotionEventCompat.getY(ev, newPointerIndex);
+ mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex);
+ }
+ break;
+ }
+ }
+ return true;
+}
+
+Use Touch to Perform Scaling
+
+private ScaleGestureDetector mScaleDetector;
+private float mScaleFactor = 1.f;
+
+public MyCustomView(Context mContext){
+ ...
+ // View code goes here
+ ...
+ mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
+}
+
+@Override
+public boolean onTouchEvent(MotionEvent ev) {
+ // Let the ScaleGestureDetector inspect all events.
+ mScaleDetector.onTouchEvent(ev);
+ return true;
+}
+
+@Override
+public void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ canvas.save();
+ canvas.scale(mScaleFactor, mScaleFactor);
+ ...
+ // onDraw() code goes here
+ ...
+ canvas.restore();
+}
+
+private class ScaleListener
+ extends ScaleGestureDetector.SimpleOnScaleGestureListener {
+ @Override
+ public boolean onScale(ScaleGestureDetector detector) {
+ mScaleFactor *= detector.getScaleFactor();
+
+ // Don't let the object get too small or too large.
+ mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 5.0f));
+
+ invalidate();
+ return true;
+ }
+}
diff --git a/docs/html/training/gestures/scroll.jd b/docs/html/training/gestures/scroll.jd
new file mode 100644
index 0000000..955495a
--- /dev/null
+++ b/docs/html/training/gestures/scroll.jd
@@ -0,0 +1,161 @@
+page.title=Animating a Scroll Gesture
+parent.title=Using Touch Gestures
+parent.link=index.html
+
+trainingnavtop=true
+next.title=Handling Multi-Touch Gestures
+next.link=multi.html
+
+@jd:body
+
+This lesson teaches you to
+
+
+
+You should also read
+
+
+
+
+
+Implement Touch-Based Scrolling
+
+
+
+private OverScroller mScroller = new OverScroller(context);
+
+private GestureDetector.SimpleOnGestureListener mGestureListener
+ = new GestureDetector.SimpleOnGestureListener() {
+ @Override
+ public boolean onDown(MotionEvent e) {
+ // Abort any active scroll animations and invalidate.
+ mScroller.forceFinished(true);
+ // There is also a compatibility version:
+ // ViewCompat.postInvalidateOnAnimation
+ postInvalidateOnAnimation();
+ return true;
+ }
+
+ @Override
+ public boolean onScroll(MotionEvent e1, MotionEvent e2,
+ float distanceX, float distanceY) {
+ // You don't use a scroller in onScroll because you don't need to animate
+ // a scroll. The scroll occurs instantly in response to touch feedback.
+ return false;
+ }
+
+ @Override
+ public boolean onFling(MotionEvent e1, MotionEvent e2,
+ float velocityX, float velocityY) {
+ // Before flinging, abort the current animation.
+ mScroller.forceFinished(true);
+ // Begin the scroll animation
+ mScroller.fling(
+ // Current scroll position
+ startX,
+ startY,
+ // Velocities, negated for natural touch response
+ (int) -velocityX,
+ (int) -velocityY,
+ // Minimum and maximum scroll positions. The minimum scroll
+ // position is generally zero and the maximum scroll position
+ // is generally the content size less the screen size. So if the
+ // content width is 1000 pixels and the screen width is 200
+ // pixels, the maximum scroll offset should be 800 pixels.
+ minX, maxX,
+ minY, maxY,
+ // The maximum overscroll bounds. This is useful when using
+ // the EdgeEffect class to draw overscroll "glow" overlays.
+ mContentRect.width() / 2,
+ mContentRect.height() / 2);
+ // Invalidate to trigger computeScroll()
+ postInvalidateOnAnimation();
+ return true;
+ }
+};
+
+@Override
+public void computeScroll() {
+ super.computeScroll();
+
+ // Compute the current scroll offsets. If this returns true, then the
+ // scroll has not yet finished.
+ if (mScroller.computeScrollOffset()) {
+ int currX = mScroller.getCurrX();
+ int currY = mScroller.getCurrY();
+
+ // Actually render the scrolled viewport, or actually scroll the
+ // view using View.scrollTo.
+
+ // If currX or currY are outside the bounds, render the overscroll
+ // glow using EdgeEffect.
+
+ } else {
+ // The scroll has finished.
+ }
+}
+
+This lesson teaches you to
+
+
+
+
+You should also read
+
+
+
+
+
+Intercept Touch Events in a ViewGroup
+
+public class MyViewGroup extends ViewGroup {
+
+ private int mTouchSlop;
+
+ ...
+
+ ViewConfiguration vc = ViewConfiguration.get(view.getContext());
+ mTouchSlop = vc.getScaledTouchSlop();
+
+ ...
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ /*
+ * This method JUST determines whether we want to intercept the motion.
+ * If we return true, onTouchEvent will be called and we do the actual
+ * scrolling there.
+ */
+
+
+ final int action = MotionEventCompat.getActionMasked(ev);
+
+ // Always handle the case of the touch gesture being complete.
+ if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
+ // Release the scroll.
+ mIsScrolling = false;
+ return false; // Do not intercept touch event, let the child handle it
+ }
+
+ switch (action) {
+ case MotionEvent.ACTION_MOVE: {
+ if (mIsScrolling) {
+ // We're currently scrolling, so yes, intercept the
+ // touch event!
+ return true;
+ }
+
+ // If the user has dragged her finger horizontally more than
+ // the touch slop, start the scroll
+
+ // left as an exercise for the reader
+ final int xDiff = calculateDistanceX(ev);
+
+ // Touch slop should be calculated using ViewConfiguration
+ // constants.
+ if (xDiff > mTouchSlop) {
+ // Start scrolling!
+ mIsScrolling = true;
+ return true;
+ }
+ break;
+ }
+ ...
+ }
+
+ // In general, we don't want to intercept touch events. They should be
+ // handled by the child view.
+ return false;
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ // Here we actually handle the touch event (e.g. if the action is ACTION_MOVE,
+ // scroll this container).
+ // This method will only be called if the touch event was intercepted in
+ // onInterceptTouchEvent
+ ...
+ }
+}
+
+Use ViewConfiguration Constants
+
+ViewConfiguration vc = ViewConfiguration.get(view.getContext());
+private int mSlop = vc.getScaledTouchSlop();
+private int mMinFlingVelocity = vc.getScaledMinimumFlingVelocity();
+private int mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity();
+
+...
+
+case MotionEvent.ACTION_MOVE: {
+ ...
+ float deltaX = motionEvent.getRawX() - mDownX;
+ if (Math.abs(deltaX) > mSlop) {
+ // A swipe occurred, do something
+ }
+
+...
+
+case MotionEvent.ACTION_UP: {
+ ...
+ } if (mMinFlingVelocity <= velocityX && velocityX <= mMaxFlingVelocity
+ && velocityY < velocityX) {
+ // The criteria have been satisfied, do something
+ }
+}
+
+
+Extend a Child View's Touchable Area
+
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/parent_layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:context=".MainActivity" >
+
+ <ImageButton android:id="@+id/button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:background="@null"
+ android:src="@drawable/icon" />
+</RelativeLayout>
+
+
+
+
+
+In its capacity as touch delegate for the {@link android.widget.ImageButton} child view, the
+parent view will receive all touch events. If the touch event occurred within the child's hit
+rectangle, the parent will pass the touch
+event to the child for handling.
+public class MainActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+ // Get the parent view
+ View parentView = findViewById(R.id.parent_layout);
+
+ parentView.post(new Runnable() {
+ // Post in the parent's message queue to make sure the parent
+ // lays out its children before you call getHitRect()
+ @Override
+ public void run() {
+ // The bounds for the delegate view (an ImageButton
+ // in this example)
+ Rect delegateArea = new Rect();
+ ImageButton myButton = (ImageButton) findViewById(R.id.button);
+ myButton.setEnabled(true);
+ myButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ Toast.makeText(MainActivity.this,
+ "Touch occurred within ImageButton touch region.",
+ Toast.LENGTH_SHORT).show();
+ }
+ });
+
+ // The hit rectangle for the ImageButton
+ myButton.getHitRect(delegateArea);
+
+ // Extend the touch area of the ImageButton beyond its bounds
+ // on the right and bottom.
+ delegateArea.right += 100;
+ delegateArea.bottom += 100;
+
+ // Instantiate a TouchDelegate.
+ // "delegateArea" is the bounds in local coordinates of
+ // the containing view to be mapped to the delegate view.
+ // "myButton" is the child view that should receive motion
+ // events.
+ TouchDelegate touchDelegate = new TouchDelegate(delegateArea,
+ myButton);
+
+ // Sets the TouchDelegate on the parent view, such that touches
+ // within the touch delegate bounds are routed to the child.
+ if (View.class.isInstance(myButton.getParent())) {
+ ((View) myButton.getParent()).setTouchDelegate(touchDelegate);
+ }
+ }
+ });
+ }
+}
+
diff --git a/docs/html/training/training_toc.cs b/docs/html/training/training_toc.cs
index c4e0f84..cdc1574 100644
--- a/docs/html/training/training_toc.cs
+++ b/docs/html/training/training_toc.cs
@@ -799,7 +799,51 @@
-
+