1page.title=Making the View Interactive
2parent.title=Creating Custom Views
3parent.link=index.html
4
5trainingnavtop=true
6previous.title=Custom Drawing
7previous.link=custom-drawing.html
8next.title=Optmizing the View
9next.link=optimizing-view.html
10
11@jd:body
12
13<div id="tb-wrapper">
14    <div id="tb">
15
16        <h2>This lesson teaches you to</h2>
17        <ol>
18            <li><a href="#inputgesture">Handle Input Gestures</a></li>
19            <li><a href="#motion">Create Physically Plausible Motion</a></li>
20            <li><a href="#makesmooth">Make Your Transitions Smooth</a></li>
21        </ol>
22
23        <h2>You should also read</h2>
24        <ul>
25            <li><a href="{@docRoot}guide/topics/ui/ui-events.html">Input Events</a></li>
26            <li><a href="{@docRoot}guide/topics/graphics/prop-animation.html">Property Animation</a>
27            </li>
28        </ul>
29<h2>Try it out</h2>
30<div class="download-box">
31<a href="{@docRoot}shareables/training/CustomView.zip"
32class="button">Download the sample</a>
33<p class="filename">CustomView.zip</p>
34</div>
35    </div>
36</div>
37
38<p>Drawing a UI is only one part of creating a custom view. You also need to make your view respond
39to user input in a
40way that closely resembles the real-world action you're mimicking. Objects should always act in the
41same way that real
42objects do. For example, images should not immediately pop out of existence and reappear somewhere
43else, because objects
44in the real world don't do that. Instead, images should move from one place to another.</p>
45
46<p>Users also sense subtle behavior or feel in an interface, and react best to subtleties that
47mimic the real world.
48For example, when users fling a UI object, they should sense friction at the beginning that delays
49the motion, and then
50at the end sense momentum that carries the motion beyond the fling.</p>
51
52<p>This lesson demonstrates how to use features of the Android framework to add these real-world
53behaviors to your
54custom view.
55
56<h2 id="inputgesture">Handle Input Gestures</h2>
57
58<p>Like many other UI frameworks, Android supports an input event model. User actions are turned
59    into events that
60    trigger callbacks, and you can override the callbacks to customize how your application responds
61    to the user. The
62    most common input event in the Android system is <em>touch</em>, which triggers {@link
63    android.view.View#onTouchEvent(android.view.MotionEvent)}. Override this method to handle the
64    event:</p>
65
66<pre>
67   &#64Override
68   public boolean onTouchEvent(MotionEvent event) {
69    return super.onTouchEvent(event);
70   }
71</pre>
72
73<p>Touch events by themselves are not particularly useful. Modern touch UIs define interactions in
74    terms of gestures
75    such as tapping, pulling, pushing, flinging, and zooming. To convert raw touch events into
76    gestures, Android
77    provides {@link android.view.GestureDetector}.</p>
78
79<p>Construct a {@link android.view.GestureDetector} by passing in an instance of a class that
80    implements {@link
81    android.view.GestureDetector.OnGestureListener}. If you only want to process a few gestures, you
82    can extend {@link
83    android.view.GestureDetector.SimpleOnGestureListener} instead of implementing the {@link
84    android.view.GestureDetector.OnGestureListener}
85    interface. For instance, this code creates a class that extends {@link
86    android.view.GestureDetector.SimpleOnGestureListener} and overrides {@link
87    android.view.GestureDetector.SimpleOnGestureListener#onDown}.</p>
88
89<pre>
90class mListener extends GestureDetector.SimpleOnGestureListener {
91   &#64;Override
92   public boolean onDown(MotionEvent e) {
93       return true;
94   }
95}
96mDetector = new GestureDetector(PieChart.this.getContext(), new mListener());
97</pre>
98
99<p>Whether or not you use {@link
100    android.view.GestureDetector.SimpleOnGestureListener}, you must always implement an
101    {@link android.view.GestureDetector.OnGestureListener#onDown onDown()} method that
102    returns {@code true}. This step is necessary because all gestures begin with an
103    {@link android.view.GestureDetector.OnGestureListener#onDown onDown()} message. If
104    you return {@code
105    false} from {@link android.view.GestureDetector.OnGestureListener#onDown onDown()}, as
106    {@link android.view.GestureDetector.SimpleOnGestureListener} does, the system assumes that
107    you want to ignore the
108    rest of the gesture, and the other methods of
109    {@link android.view.GestureDetector.OnGestureListener} never get called. The
110    only time you should
111    return {@code false} from {@link android.view.GestureDetector.OnGestureListener#onDown onDown()}
112    is if you truly want to ignore an entire gesture.
113
114    Once you've implemented {@link android.view.GestureDetector.OnGestureListener}
115    and created an instance of {@link android.view.GestureDetector}, you can use
116    your {@link android.view.GestureDetector} to interpret the touch events you receive in {@link
117    android.view.GestureDetector#onTouchEvent onTouchEvent()}.</p>
118
119<pre>
120&#64;Override
121public boolean onTouchEvent(MotionEvent event) {
122   boolean result = mDetector.onTouchEvent(event);
123   if (!result) {
124       if (event.getAction() == MotionEvent.ACTION_UP) {
125           stopScrolling();
126           result = true;
127       }
128   }
129   return result;
130}
131</pre>
132
133<p>When you pass {@link android.view.GestureDetector#onTouchEvent onTouchEvent()} a touch event that
134    it doesn't
135    recognize as part of a gesture, it returns {@code false}. You can then run your own custom
136    gesture-detection
137    code.</p>
138
139<h2 id="motion">Create Physically Plausible Motion</h2>
140
141<p>Gestures are a powerful way to control touchscreen devices, but they can be counterintuitive and
142    difficult to
143    remember unless they produce physically plausible results. A good example of this is the <em>fling</em>
144    gesture, where the
145    user quickly moves a finger across the screen and then lifts it. This gesture makes sense if the UI
146    responds by moving
147    quickly in the direction of the fling, then slowing down, as if the user had pushed on a
148    flywheel and set it
149    spinning.</p>
150
151<p>However, simulating the feel of a flywheel isn't trivial. A lot of physics and math are required
152    to get a flywheel
153    model working correctly. Fortunately, Android provides helper classes to simulate this and other
154    behaviors. The
155    {@link android.widget.Scroller} class is the basis for handling flywheel-style <em>fling</em>
156    gestures.</p>
157
158<p>To start a fling, call {@link android.widget.Scroller#fling fling()} with the starting velocity
159    and the minimum and
160    maximum x and y values of the fling. For the velocity value, you can use the value computed for
161    you by {@link android.view.GestureDetector}.</p>
162
163<pre>
164&#64;Override
165public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
166   mScroller.fling(currentX, currentY, velocityX / SCALE, velocityY / SCALE, minX, minY, maxX, maxY);
167   postInvalidate();
168}
169</pre>
170
171<p class="note"><strong>Note:</strong> Although the velocity calculated by
172    {@link android.view.GestureDetector} is physically accurate,
173    many developers feel
174    that using this value makes the fling animation too fast. It's common to divide the x and y
175    velocity by a factor of
176    4 to 8.</p>
177
178<p>The call to {@link android.widget.Scroller#fling fling()} sets up the physics model for the fling
179    gesture.
180    Afterwards, you need to update the {@link android.widget.Scroller Scroller} by calling {@link
181    android.widget.Scroller#computeScrollOffset Scroller.computeScrollOffset()} at regular
182    intervals. {@link
183    android.widget.Scroller#computeScrollOffset computeScrollOffset()} updates the {@link
184    android.widget.Scroller
185    Scroller} object's internal state by reading the current time and using the physics model to calculate
186    the x and y position
187    at that time. Call {@link android.widget.Scroller#getCurrX} and {@link
188    android.widget.Scroller#getCurrY} to
189    retrieve these values.</p>
190
191<p>Most views pass the {@link android.widget.Scroller Scroller} object's x and y position directly to
192    {@link
193    android.view.View#scrollTo scrollTo()}. The PieChart example is a little different: it
194    uses the current scroll
195    y position to set the rotational angle of the chart.</p>
196
197<pre>
198if (!mScroller.isFinished()) {
199    mScroller.computeScrollOffset();
200    setPieRotation(mScroller.getCurrY());
201}
202</pre>
203
204<p>The {@link android.widget.Scroller Scroller} class computes scroll positions for you, but it does
205    not automatically
206    apply those positions to your view. It's your responsibility to make sure you get and apply new
207    coordinates often
208    enough to make the scrolling animation look smooth. There are two ways to do this:</p>
209
210<ul>
211    <li>Call {@link android.view.View#postInvalidate() postInvalidate()} after calling
212        {@link android.widget.Scroller#fling(int, int, int, int, int, int, int, int) fling()},
213        in order to
214        force a redraw. This
215        technique requires that you compute scroll offsets in {@link android.view.View#onDraw onDraw()}
216        and call {@link android.view.View#postInvalidate() postInvalidate()} every
217        time the scroll offset changes.
218    </li>
219    <li>Set up a {@link android.animation.ValueAnimator} to animate for the duration of the fling,
220        and add a listener to process animation updates
221        by calling {@link android.animation.ValueAnimator#addUpdateListener addUpdateListener()}.
222    </li>
223</ul>
224
225<p>The PieChart example uses the second approach. This technique is slightly more complex to set up, but
226    it works more
227    closely with the animation system and doesn't require potentially unnecessary view
228    invalidation. The drawback is that {@link android.animation.ValueAnimator}
229    is not available prior to API level 11, so this technique cannot be used
230on devices running Android versions lower than 3.0.</p>
231
232<p class="note"><strong>Note:</strong> You can use {@link android.animation.ValueAnimator} in
233applications that
234target lower API levels. You just need to make sure to check the current API level
235at runtime, and omit the calls to the view animation system if the current level is less than 11.</p>
236
237<pre>
238       mScroller = new Scroller(getContext(), null, true);
239       mScrollAnimator = ValueAnimator.ofFloat(0,1);
240       mScrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
241           &#64;Override
242           public void onAnimationUpdate(ValueAnimator valueAnimator) {
243               if (!mScroller.isFinished()) {
244                   mScroller.computeScrollOffset();
245                   setPieRotation(mScroller.getCurrY());
246               } else {
247                   mScrollAnimator.cancel();
248                   onScrollFinished();
249               }
250           }
251       });
252</pre>
253
254<h2 id="makesmooth">Make Your Transitions Smooth</h2>
255
256<p>Users expect a modern UI to transition smoothly between states. UI elements fade in and out
257    instead of appearing and
258    disappearing. Motions begin and end smoothly instead of starting and stopping abruptly. The
259    Android <a
260            href="{@docRoot}guide/topics/graphics/prop-animation.html">property animation
261        framework</a>, introduced in
262    Android 3.0, makes smooth transitions easy.</p>
263
264<p>To use the animation system, whenever a property changes that will affect your view's appearance,
265    do not change the
266    property directly. Instead, use {@link android.animation.ValueAnimator} to make the change. In
267    the following
268    example, modifying the
269    currently selected pie slice in PieChart causes the entire chart to rotate so that the selection
270    pointer is centered
271    in the selected slice. {@link android.animation.ValueAnimator} changes the rotation over a
272    period of several
273    hundred milliseconds,
274    rather than immediately setting the new rotation value.</p>
275
276<pre>
277mAutoCenterAnimator = ObjectAnimator.ofInt(PieChart.this, "PieRotation", 0);
278mAutoCenterAnimator.setIntValues(targetAngle);
279mAutoCenterAnimator.setDuration(AUTOCENTER_ANIM_DURATION);
280mAutoCenterAnimator.start();
281</pre>
282
283<p>If the value you want to change is one of the base {@link android.view.View} properties, doing
284    the animation
285    is even easier,
286    because Views have a built-in {@link android.view.ViewPropertyAnimator} that is optimized for
287    simultaneous animation
288    of multiple properties. For example:</p>
289
290<pre>
291animate().rotation(targetAngle).setDuration(ANIM_DURATION).start();
292</pre>
293