blob: 17e40858588a3ee12942f385cf176a1420043aed [file] [log] [blame]
&& repo sync -j8518edbf2012-11-30 16:28:27 -08001page.title=Dragging and Scaling
2parent.title=Using Touch Gestures
3parent.link=index.html
4
5trainingnavtop=true
6next.title=Managing Touch Events in a ViewGroup
7next.link=viewgroup.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="#drag">Drag an Object</a></li>
18 <li><a href="#scale">Use Touch to Perform Scaling</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>
28 <li><a href="http://android-developers.blogspot.com/2010/06/making-sense-of-multitouch.html">Making Sense of Multitouch</a> blog post</li>
29 <li><a href="{@docRoot}training/custom-views/making-interactive.html">Making the View Interactive</a> </li>
30 <li>Design Guide for <a href="{@docRoot}design/patterns/gestures.html">Gestures</a></li>
31 <li>Design Guide for <a href="{@docRoot}design/style/touch-feedback.html">Touch Feedback</a></li>
32</ul>
33
34
35</div>
36</div>
37<p>This lesson describes how to use touch gestures to drag and scale on-screen
38objects, using {@link android.view.View#onTouchEvent onTouchEvent()} to intercept
39touch events. Here is the original <a
40href="http://code.google.com/p/android-touchexample/">source code</a>
41for the examples used in this lesson.
42</p>
43
44<h2 id="drag">Drag an Object</h2>
45
46<p class="note">If you are targeting Android 3.0 or higher, you can use the built-in drag-and-drop event
47listeners with {@link android.view.View.OnDragListener}, as described in
48<a href="{@docRoot}guide/topics/ui/drag-drop.html">Drag and Drop</a>.
49
50<p>A common operation for a touch gesture is to use it to drag an object across
51the screen. The following snippet lets the user drag an on-screen image. Note
52the following:</p>
53
54<ul>
55
56<li>In a drag (or scroll) operation, the app has to keep track of the original pointer
57(finger), even if additional fingers get placed on the screen. For example,
58imagine that while dragging the image around, the user places a second finger on
59the touch screen and lifts the first finger. If your app is just tracking
60individual pointers, it will regard the second pointer as the default and move
61the image to that location.</li>
62
63<li>To prevent this from happening, your app needs to distinguish between the
64original pointer and any follow-on pointers. To do this, it tracks the
65{@link android.view.MotionEvent#ACTION_POINTER_DOWN} and
66{@link android.view.MotionEvent#ACTION_POINTER_UP} events described in
67<a href="multi.html">Handling Multi-Touch Gestures</a>.
68{@link android.view.MotionEvent#ACTION_POINTER_DOWN} and
69{@link android.view.MotionEvent#ACTION_POINTER_UP} are
70passed to the {@link android.view.View#onTouchEvent onTouchEvent()} callback
71whenever a secondary pointer goes down or up. </li>
72
73
74<li>In the {@link android.view.MotionEvent#ACTION_POINTER_UP} case, the example
75extracts this index and ensures that the active pointer ID is not referring to a
76pointer that is no longer touching the screen. If it is, the app selects a
77different pointer to be active and saves its current X and Y position. Since
78this saved position is used in the {@link android.view.MotionEvent#ACTION_MOVE}
79case to calculate the distance to move the onscreen object, the app will always
80calculate the distance to move using data from the correct pointer.</li>
81
82</ul>
83
84<p>The following snippet enables a user to drag an object around on the screen. It records the initial
85position of the active pointer, calculates the distance the pointer traveled, and moves the object to the
86new position. It correctly manages the possibility of additional pointers, as described
87above.</p>
88
89<p>Notice that the snippet uses the {@link android.view.MotionEvent#getActionMasked getActionMasked()} method.
90You should always use this method (or better yet, the compatability version
91{@link android.support.v4.view.MotionEventCompat#getActionMasked MotionEventCompat.getActionMasked()})
92to retrieve the action of a
93{@link android.view.MotionEvent}. Unlike the older
94{@link android.view.MotionEvent#getAction getAction()}
95method, {@link android.support.v4.view.MotionEventCompat#getActionMasked getActionMasked()}
96is designed to work with multiple pointers. It returns the masked action
97being performed, without including the pointer index bits.</p>
98
99<pre>// The ‘active pointer’ is the one currently moving our object.
100private int mActivePointerId = INVALID_POINTER_ID;
101
102&#64;Override
103public boolean onTouchEvent(MotionEvent ev) {
104 // Let the ScaleGestureDetector inspect all events.
105 mScaleDetector.onTouchEvent(ev);
106
107 final int action = MotionEventCompat.getActionMasked(ev);
108
109 switch (action) {
110 case MotionEvent.ACTION_DOWN: {
111 final int pointerIndex = MotionEventCompat.getActionIndex(ev);
112 final float x = MotionEventCompat.getX(ev, pointerIndex);
113 final float y = MotionEventCompat.getY(ev, pointerIndex);
114
115 // Remember where we started (for dragging)
116 mLastTouchX = x;
117 mLastTouchY = y;
118 // Save the ID of this pointer (for dragging)
119 mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
120 break;
121 }
122
123 case MotionEvent.ACTION_MOVE: {
124 // Find the index of the active pointer and fetch its position
125 final int pointerIndex =
126 MotionEventCompat.findPointerIndex(ev, mActivePointerId);
127
128 final float x = MotionEventCompat.getX(ev, pointerIndex);
129 final float y = MotionEventCompat.getY(ev, pointerIndex);
130
131 // Only move if the ScaleGestureDetector isn't processing a gesture.
132 if (!mScaleDetector.isInProgress()) {
133 // Calculate the distance moved
134 final float dx = x - mLastTouchX;
135 final float dy = y - mLastTouchY;
136
137 mPosX += dx;
138 mPosY += dy;
139
140 invalidate();
141 }
142 // Remember this touch position for the next move event
143 mLastTouchX = x;
144 mLastTouchY = y;
145
146 break;
147 }
148
149 case MotionEvent.ACTION_UP: {
150 mActivePointerId = INVALID_POINTER_ID;
151 break;
152 }
153
154 case MotionEvent.ACTION_CANCEL: {
155 mActivePointerId = INVALID_POINTER_ID;
156 break;
157 }
158
159 case MotionEvent.ACTION_POINTER_UP: {
160
161 final int pointerIndex = MotionEventCompat.getActionIndex(ev);
162 final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex);
163
164 if (pointerId == mActivePointerId) {
165 // This was our active pointer going up. Choose a new
166 // active pointer and adjust accordingly.
167 final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
168 mLastTouchX = MotionEventCompat.getX(ev, newPointerIndex);
169 mLastTouchY = MotionEventCompat.getY(ev, newPointerIndex);
170 mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex);
171 }
172 break;
173 }
174 }
175 return true;
176}</pre>
177
178<h2 id="scale">Use Touch to Perform Scaling</h2>
179
180<p>As discussed in <a href="detector.html">Detecting Common Gestures</a>,
181{@link android.view.GestureDetector} helps you detect common gestures used by
182Android such as scrolling, flinging, and long press. For scaling, Android
183provides {@link android.view.ScaleGestureDetector}. {@link
184android.view.GestureDetector} and {@link android.view.ScaleGestureDetector} can
185be used together when you want a view to recognize additional gestures.</p>
186
187<p>To report detected gesture events, gesture detectors use listener objects
188passed to their constructors. {@link android.view.ScaleGestureDetector} uses
189{@link android.view.ScaleGestureDetector.OnScaleGestureListener}.
190Android provides
191{@link android.view.ScaleGestureDetector.SimpleOnScaleGestureListener}
192as a helper class that you can extend if you dont care about all of the reported events.</p>
193
194<p>Here is a snippet that gives you the basic idea of how to perform scaling.
195Here is the original <a
196href="http://code.google.com/p/android-touchexample/">source code</a>
197for the examples.</p>
198
199<pre>private ScaleGestureDetector mScaleDetector;
200private float mScaleFactor = 1.f;
201
202public MyCustomView(Context mContext){
203 ...
204 // View code goes here
205 ...
206 mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
207}
208
209&#64;Override
210public boolean onTouchEvent(MotionEvent ev) {
211 // Let the ScaleGestureDetector inspect all events.
212 mScaleDetector.onTouchEvent(ev);
213 return true;
214}
215
216&#64;Override
217public void onDraw(Canvas canvas) {
218 super.onDraw(canvas);
219
220 canvas.save();
221 canvas.scale(mScaleFactor, mScaleFactor);
222 ...
223 // onDraw() code goes here
224 ...
225 canvas.restore();
226}
227
228private class ScaleListener
229 extends ScaleGestureDetector.SimpleOnScaleGestureListener {
230 &#64;Override
231 public boolean onScale(ScaleGestureDetector detector) {
232 mScaleFactor *= detector.getScaleFactor();
233
234 // Don't let the object get too small or too large.
235 mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 5.0f));
236
237 invalidate();
238 return true;
239 }
240}</pre>