• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.support.v4.widget;
18 
19 import android.content.Context;
20 import android.os.Build;
21 import android.view.animation.AnimationUtils;
22 import android.view.animation.Interpolator;
23 import android.widget.Scroller;
24 
25 /**
26  * Provides access to new {@link android.widget.Scroller Scroller} APIs when available.
27  *
28  * <p>This class provides a platform version-independent mechanism for obeying the
29  * current device's preferred scroll physics and fling behavior. It offers a subset of
30  * the APIs from Scroller or OverScroller.</p>
31  */
32 public class ScrollerCompat {
33     private static final String TAG = "ScrollerCompat";
34 
35     Object mScroller;
36     ScrollerCompatImpl mImpl;
37 
38     interface ScrollerCompatImpl {
createScroller(Context context, Interpolator interpolator)39         Object createScroller(Context context, Interpolator interpolator);
isFinished(Object scroller)40         boolean isFinished(Object scroller);
getCurrX(Object scroller)41         int getCurrX(Object scroller);
getCurrY(Object scroller)42         int getCurrY(Object scroller);
getCurrVelocity(Object scroller)43         float getCurrVelocity(Object scroller);
computeScrollOffset(Object scroller)44         boolean computeScrollOffset(Object scroller);
startScroll(Object scroller, int startX, int startY, int dx, int dy)45         void startScroll(Object scroller, int startX, int startY, int dx, int dy);
startScroll(Object scroller, int startX, int startY, int dx, int dy, int duration)46         void startScroll(Object scroller, int startX, int startY, int dx, int dy, int duration);
fling(Object scroller, int startX, int startY, int velX, int velY, int minX, int maxX, int minY, int maxY)47         void fling(Object scroller, int startX, int startY, int velX, int velY,
48                 int minX, int maxX, int minY, int maxY);
fling(Object scroller, int startX, int startY, int velX, int velY, int minX, int maxX, int minY, int maxY, int overX, int overY)49         void fling(Object scroller, int startX, int startY, int velX, int velY,
50                 int minX, int maxX, int minY, int maxY, int overX, int overY);
abortAnimation(Object scroller)51         void abortAnimation(Object scroller);
notifyHorizontalEdgeReached(Object scroller, int startX, int finalX, int overX)52         void notifyHorizontalEdgeReached(Object scroller, int startX, int finalX, int overX);
notifyVerticalEdgeReached(Object scroller, int startY, int finalY, int overY)53         void notifyVerticalEdgeReached(Object scroller, int startY, int finalY, int overY);
isOverScrolled(Object scroller)54         boolean isOverScrolled(Object scroller);
getFinalX(Object scroller)55         int getFinalX(Object scroller);
getFinalY(Object scroller)56         int getFinalY(Object scroller);
57     }
58 
59     static final int CHASE_FRAME_TIME = 16; // ms per target frame
60 
61     static class ScrollerCompatImplBase implements ScrollerCompatImpl {
62         @Override
createScroller(Context context, Interpolator interpolator)63         public Object createScroller(Context context, Interpolator interpolator) {
64             return interpolator != null ?
65                     new Scroller(context, interpolator) : new Scroller(context);
66         }
67 
68         @Override
isFinished(Object scroller)69         public boolean isFinished(Object scroller) {
70             return ((Scroller) scroller).isFinished();
71         }
72 
73         @Override
getCurrX(Object scroller)74         public int getCurrX(Object scroller) {
75             return ((Scroller) scroller).getCurrX();
76         }
77 
78         @Override
getCurrY(Object scroller)79         public int getCurrY(Object scroller) {
80             return ((Scroller) scroller).getCurrY();
81         }
82 
83         @Override
getCurrVelocity(Object scroller)84         public float getCurrVelocity(Object scroller) {
85             return 0;
86         }
87 
88         @Override
computeScrollOffset(Object scroller)89         public boolean computeScrollOffset(Object scroller) {
90             final Scroller s = (Scroller) scroller;
91             return s.computeScrollOffset();
92         }
93 
94         @Override
startScroll(Object scroller, int startX, int startY, int dx, int dy)95         public void startScroll(Object scroller, int startX, int startY, int dx, int dy) {
96             ((Scroller) scroller).startScroll(startX, startY, dx, dy);
97         }
98 
99         @Override
startScroll(Object scroller, int startX, int startY, int dx, int dy, int duration)100         public void startScroll(Object scroller, int startX, int startY, int dx, int dy,
101                 int duration) {
102             ((Scroller) scroller).startScroll(startX, startY, dx, dy, duration);
103         }
104 
105         @Override
fling(Object scroller, int startX, int startY, int velX, int velY, int minX, int maxX, int minY, int maxY)106         public void fling(Object scroller, int startX, int startY, int velX, int velY,
107                 int minX, int maxX, int minY, int maxY) {
108             ((Scroller) scroller).fling(startX, startY, velX, velY, minX, maxX, minY, maxY);
109         }
110 
111         @Override
fling(Object scroller, int startX, int startY, int velX, int velY, int minX, int maxX, int minY, int maxY, int overX, int overY)112         public void fling(Object scroller, int startX, int startY, int velX, int velY,
113                 int minX, int maxX, int minY, int maxY, int overX, int overY) {
114             ((Scroller) scroller).fling(startX, startY, velX, velY, minX, maxX, minY, maxY);
115         }
116 
117         @Override
abortAnimation(Object scroller)118         public void abortAnimation(Object scroller) {
119             ((Scroller) scroller).abortAnimation();
120         }
121 
122         @Override
notifyHorizontalEdgeReached(Object scroller, int startX, int finalX, int overX)123         public void notifyHorizontalEdgeReached(Object scroller, int startX, int finalX,
124                 int overX) {
125             // No-op
126         }
127 
128         @Override
notifyVerticalEdgeReached(Object scroller, int startY, int finalY, int overY)129         public void notifyVerticalEdgeReached(Object scroller, int startY, int finalY, int overY) {
130             // No-op
131         }
132 
133         @Override
isOverScrolled(Object scroller)134         public boolean isOverScrolled(Object scroller) {
135             // Always false
136             return false;
137         }
138 
139         @Override
getFinalX(Object scroller)140         public int getFinalX(Object scroller) {
141             return ((Scroller) scroller).getFinalX();
142         }
143 
144         @Override
getFinalY(Object scroller)145         public int getFinalY(Object scroller) {
146             return ((Scroller) scroller).getFinalY();
147         }
148     }
149 
150     static class ScrollerCompatImplGingerbread implements ScrollerCompatImpl {
151         @Override
createScroller(Context context, Interpolator interpolator)152         public Object createScroller(Context context, Interpolator interpolator) {
153             return ScrollerCompatGingerbread.createScroller(context, interpolator);
154         }
155 
156         @Override
isFinished(Object scroller)157         public boolean isFinished(Object scroller) {
158             return ScrollerCompatGingerbread.isFinished(scroller);
159         }
160 
161         @Override
getCurrX(Object scroller)162         public int getCurrX(Object scroller) {
163             return ScrollerCompatGingerbread.getCurrX(scroller);
164         }
165 
166         @Override
getCurrY(Object scroller)167         public int getCurrY(Object scroller) {
168             return ScrollerCompatGingerbread.getCurrY(scroller);
169         }
170 
171         @Override
getCurrVelocity(Object scroller)172         public float getCurrVelocity(Object scroller) {
173             return 0;
174         }
175 
176         @Override
computeScrollOffset(Object scroller)177         public boolean computeScrollOffset(Object scroller) {
178             return ScrollerCompatGingerbread.computeScrollOffset(scroller);
179         }
180 
181         @Override
startScroll(Object scroller, int startX, int startY, int dx, int dy)182         public void startScroll(Object scroller, int startX, int startY, int dx, int dy) {
183             ScrollerCompatGingerbread.startScroll(scroller, startX, startY, dx, dy);
184         }
185 
186         @Override
startScroll(Object scroller, int startX, int startY, int dx, int dy, int duration)187         public void startScroll(Object scroller, int startX, int startY, int dx, int dy,
188                 int duration) {
189             ScrollerCompatGingerbread.startScroll(scroller, startX, startY, dx, dy, duration);
190         }
191 
192         @Override
fling(Object scroller, int startX, int startY, int velX, int velY, int minX, int maxX, int minY, int maxY)193         public void fling(Object scroller, int startX, int startY, int velX, int velY,
194                 int minX, int maxX, int minY, int maxY) {
195             ScrollerCompatGingerbread.fling(scroller, startX, startY, velX, velY,
196                     minX, maxX, minY, maxY);
197         }
198 
199         @Override
fling(Object scroller, int startX, int startY, int velX, int velY, int minX, int maxX, int minY, int maxY, int overX, int overY)200         public void fling(Object scroller, int startX, int startY, int velX, int velY,
201                 int minX, int maxX, int minY, int maxY, int overX, int overY) {
202             ScrollerCompatGingerbread.fling(scroller, startX, startY, velX, velY,
203                     minX, maxX, minY, maxY, overX, overY);
204         }
205 
206         @Override
abortAnimation(Object scroller)207         public void abortAnimation(Object scroller) {
208             ScrollerCompatGingerbread.abortAnimation(scroller);
209         }
210 
211         @Override
notifyHorizontalEdgeReached(Object scroller, int startX, int finalX, int overX)212         public void notifyHorizontalEdgeReached(Object scroller, int startX, int finalX,
213                 int overX) {
214             ScrollerCompatGingerbread.notifyHorizontalEdgeReached(scroller, startX, finalX, overX);
215         }
216 
217         @Override
notifyVerticalEdgeReached(Object scroller, int startY, int finalY, int overY)218         public void notifyVerticalEdgeReached(Object scroller, int startY, int finalY, int overY) {
219             ScrollerCompatGingerbread.notifyVerticalEdgeReached(scroller, startY, finalY, overY);
220         }
221 
222         @Override
isOverScrolled(Object scroller)223         public boolean isOverScrolled(Object scroller) {
224             return ScrollerCompatGingerbread.isOverScrolled(scroller);
225         }
226 
227         @Override
getFinalX(Object scroller)228         public int getFinalX(Object scroller) {
229             return ScrollerCompatGingerbread.getFinalX(scroller);
230         }
231 
232         @Override
getFinalY(Object scroller)233         public int getFinalY(Object scroller) {
234             return ScrollerCompatGingerbread.getFinalY(scroller);
235         }
236     }
237 
238     static class ScrollerCompatImplIcs extends ScrollerCompatImplGingerbread {
239         @Override
getCurrVelocity(Object scroller)240         public float getCurrVelocity(Object scroller) {
241             return ScrollerCompatIcs.getCurrVelocity(scroller);
242         }
243     }
244 
create(Context context)245     public static ScrollerCompat create(Context context) {
246         return create(context, null);
247     }
248 
create(Context context, Interpolator interpolator)249     public static ScrollerCompat create(Context context, Interpolator interpolator) {
250         return new ScrollerCompat(context, interpolator);
251     }
252 
ScrollerCompat(Context context, Interpolator interpolator)253     ScrollerCompat(Context context, Interpolator interpolator) {
254         this(Build.VERSION.SDK_INT, context, interpolator);
255 
256     }
257 
258     /**
259      * Private constructer where API version can be provided.
260      * Useful for unit testing.
261      */
ScrollerCompat(int apiVersion, Context context, Interpolator interpolator)262     private ScrollerCompat(int apiVersion, Context context, Interpolator interpolator) {
263         if (apiVersion >= 14) { // ICS
264             mImpl = new ScrollerCompatImplIcs();
265         } else if (apiVersion>= 9) { // Gingerbread
266             mImpl = new ScrollerCompatImplGingerbread();
267         } else {
268             mImpl = new ScrollerCompatImplBase();
269         }
270         mScroller = mImpl.createScroller(context, interpolator);
271     }
272 
273     /**
274      * Returns whether the scroller has finished scrolling.
275      *
276      * @return True if the scroller has finished scrolling, false otherwise.
277      */
isFinished()278     public boolean isFinished() {
279         return mImpl.isFinished(mScroller);
280     }
281 
282     /**
283      * Returns the current X offset in the scroll.
284      *
285      * @return The new X offset as an absolute distance from the origin.
286      */
getCurrX()287     public int getCurrX() {
288         return mImpl.getCurrX(mScroller);
289     }
290 
291     /**
292      * Returns the current Y offset in the scroll.
293      *
294      * @return The new Y offset as an absolute distance from the origin.
295      */
getCurrY()296     public int getCurrY() {
297         return mImpl.getCurrY(mScroller);
298     }
299 
300     /**
301      * @return The final X position for the scroll in progress, if known.
302      */
getFinalX()303     public int getFinalX() {
304         return mImpl.getFinalX(mScroller);
305     }
306 
307     /**
308      * @return The final Y position for the scroll in progress, if known.
309      */
getFinalY()310     public int getFinalY() {
311         return mImpl.getFinalY(mScroller);
312     }
313 
314     /**
315      * Returns the current velocity on platform versions that support it.
316      *
317      * <p>The device must support at least API level 14 (Ice Cream Sandwich).
318      * On older platform versions this method will return 0. This method should
319      * only be used as input for nonessential visual effects such as {@link EdgeEffectCompat}.</p>
320      *
321      * @return The original velocity less the deceleration. Result may be
322      * negative.
323      */
getCurrVelocity()324     public float getCurrVelocity() {
325         return mImpl.getCurrVelocity(mScroller);
326     }
327 
328     /**
329      * Call this when you want to know the new location.  If it returns true,
330      * the animation is not yet finished.  loc will be altered to provide the
331      * new location.
332      */
computeScrollOffset()333     public boolean computeScrollOffset() {
334         return mImpl.computeScrollOffset(mScroller);
335     }
336 
337     /**
338      * Start scrolling by providing a starting point and the distance to travel.
339      * The scroll will use the default value of 250 milliseconds for the
340      * duration.
341      *
342      * @param startX Starting horizontal scroll offset in pixels. Positive
343      *        numbers will scroll the content to the left.
344      * @param startY Starting vertical scroll offset in pixels. Positive numbers
345      *        will scroll the content up.
346      * @param dx Horizontal distance to travel. Positive numbers will scroll the
347      *        content to the left.
348      * @param dy Vertical distance to travel. Positive numbers will scroll the
349      *        content up.
350      */
startScroll(int startX, int startY, int dx, int dy)351     public void startScroll(int startX, int startY, int dx, int dy) {
352         mImpl.startScroll(mScroller, startX, startY, dx, dy);
353     }
354 
355     /**
356      * Start scrolling by providing a starting point and the distance to travel.
357      *
358      * @param startX Starting horizontal scroll offset in pixels. Positive
359      *        numbers will scroll the content to the left.
360      * @param startY Starting vertical scroll offset in pixels. Positive numbers
361      *        will scroll the content up.
362      * @param dx Horizontal distance to travel. Positive numbers will scroll the
363      *        content to the left.
364      * @param dy Vertical distance to travel. Positive numbers will scroll the
365      *        content up.
366      * @param duration Duration of the scroll in milliseconds.
367      */
startScroll(int startX, int startY, int dx, int dy, int duration)368     public void startScroll(int startX, int startY, int dx, int dy, int duration) {
369         mImpl.startScroll(mScroller, startX, startY, dx, dy, duration);
370     }
371 
372     /**
373      * Start scrolling based on a fling gesture. The distance travelled will
374      * depend on the initial velocity of the fling.
375      *
376      * @param startX Starting point of the scroll (X)
377      * @param startY Starting point of the scroll (Y)
378      * @param velocityX Initial velocity of the fling (X) measured in pixels per
379      *        second.
380      * @param velocityY Initial velocity of the fling (Y) measured in pixels per
381      *        second
382      * @param minX Minimum X value. The scroller will not scroll past this
383      *        point.
384      * @param maxX Maximum X value. The scroller will not scroll past this
385      *        point.
386      * @param minY Minimum Y value. The scroller will not scroll past this
387      *        point.
388      * @param maxY Maximum Y value. The scroller will not scroll past this
389      *        point.
390      */
fling(int startX, int startY, int velocityX, int velocityY, int minX, int maxX, int minY, int maxY)391     public void fling(int startX, int startY, int velocityX, int velocityY,
392             int minX, int maxX, int minY, int maxY) {
393         mImpl.fling(mScroller, startX, startY, velocityX, velocityY, minX, maxX, minY, maxY);
394     }
395 
396     /**
397      * Start scrolling based on a fling gesture. The distance travelled will
398      * depend on the initial velocity of the fling.
399      *
400      * @param startX Starting point of the scroll (X)
401      * @param startY Starting point of the scroll (Y)
402      * @param velocityX Initial velocity of the fling (X) measured in pixels per
403      *        second.
404      * @param velocityY Initial velocity of the fling (Y) measured in pixels per
405      *        second
406      * @param minX Minimum X value. The scroller will not scroll past this
407      *        point.
408      * @param maxX Maximum X value. The scroller will not scroll past this
409      *        point.
410      * @param minY Minimum Y value. The scroller will not scroll past this
411      *        point.
412      * @param maxY Maximum Y value. The scroller will not scroll past this
413      *        point.
414      * @param overX Overfling range. If > 0, horizontal overfling in either
415      *            direction will be possible.
416      * @param overY Overfling range. If > 0, vertical overfling in either
417      *            direction will be possible.
418      */
fling(int startX, int startY, int velocityX, int velocityY, int minX, int maxX, int minY, int maxY, int overX, int overY)419     public void fling(int startX, int startY, int velocityX, int velocityY,
420             int minX, int maxX, int minY, int maxY, int overX, int overY) {
421         mImpl.fling(mScroller, startX, startY, velocityX, velocityY,
422                 minX, maxX, minY, maxY, overX, overY);
423     }
424 
425     /**
426      * Stops the animation. Aborting the animation causes the scroller to move to the final x and y
427      * position.
428      */
abortAnimation()429     public void abortAnimation() {
430         mImpl.abortAnimation(mScroller);
431     }
432 
433 
434     /**
435      * Notify the scroller that we've reached a horizontal boundary.
436      * Normally the information to handle this will already be known
437      * when the animation is started, such as in a call to one of the
438      * fling functions. However there are cases where this cannot be known
439      * in advance. This function will transition the current motion and
440      * animate from startX to finalX as appropriate.
441      *
442      * @param startX Starting/current X position
443      * @param finalX Desired final X position
444      * @param overX Magnitude of overscroll allowed. This should be the maximum
445      *              desired distance from finalX. Absolute value - must be positive.
446      */
notifyHorizontalEdgeReached(int startX, int finalX, int overX)447     public void notifyHorizontalEdgeReached(int startX, int finalX, int overX) {
448         mImpl.notifyHorizontalEdgeReached(mScroller, startX, finalX, overX);
449     }
450 
451     /**
452      * Notify the scroller that we've reached a vertical boundary.
453      * Normally the information to handle this will already be known
454      * when the animation is started, such as in a call to one of the
455      * fling functions. However there are cases where this cannot be known
456      * in advance. This function will animate a parabolic motion from
457      * startY to finalY.
458      *
459      * @param startY Starting/current Y position
460      * @param finalY Desired final Y position
461      * @param overY Magnitude of overscroll allowed. This should be the maximum
462      *              desired distance from finalY. Absolute value - must be positive.
463      */
notifyVerticalEdgeReached(int startY, int finalY, int overY)464     public void notifyVerticalEdgeReached(int startY, int finalY, int overY) {
465         mImpl.notifyVerticalEdgeReached(mScroller, startY, finalY, overY);
466     }
467 
468     /**
469      * Returns whether the current Scroller is currently returning to a valid position.
470      * Valid bounds were provided by the
471      * {@link #fling(int, int, int, int, int, int, int, int, int, int)} method.
472      *
473      * One should check this value before calling
474      * {@link #startScroll(int, int, int, int)} as the interpolation currently in progress
475      * to restore a valid position will then be stopped. The caller has to take into account
476      * the fact that the started scroll will start from an overscrolled position.
477      *
478      * @return true when the current position is overscrolled and in the process of
479      *         interpolating back to a valid value.
480      */
isOverScrolled()481     public boolean isOverScrolled() {
482         return mImpl.isOverScrolled(mScroller);
483     }
484 }
485