88import android .os .Build .VERSION_CODES ;
99import android .support .annotation .NonNull ;
1010import android .support .annotation .Nullable ;
11+ import android .support .v4 .view .ViewCompat ;
1112import android .support .v7 .widget .RecyclerView ;
1213import android .support .v7 .widget .RecyclerView .OnScrollListener ;
1314import android .util .AttributeSet ;
1415import android .view .LayoutInflater ;
1516import android .view .MotionEvent ;
1617import android .view .View ;
18+ import android .view .animation .Animation ;
1719import android .widget .FrameLayout ;
1820import android .widget .SectionIndexer ;
1921
3133public abstract class AbsRecyclerViewFastScroller extends FrameLayout implements RecyclerViewScroller {
3234
3335 private static final int [] STYLEABLE = R .styleable .AbsRecyclerViewFastScroller ;
36+
37+ private static final int AUTO_HIDE_SCROLLER_TIMEOUT_MILLS = 1000 ;
38+
39+ private static final int CURRENT_ANIMATION_NONE = 0 ;
40+ private static final int CURRENT_ANIMATION_SHOW = 1 ;
41+ private static final int CURRENT_ANIMATION_HIDE = 2 ;
42+
43+
3444 /** The long bar along which a handle travels */
3545 protected final View mBar ;
3646 /** The handle that signifies the user's progress in the list */
@@ -47,6 +57,15 @@ public abstract class AbsRecyclerViewFastScroller extends FrameLayout implements
4757 * {@link OnScrollListener} an abstract class instead of an interface. Hmmm */
4858 protected OnScrollListener mOnScrollListener ;
4959
60+ /** true: user is grabbing the handle, false: otherwise */
61+ private boolean mIsGrabbingHandle ;
62+
63+ /** true: always show the scroller, false: hide the scroller automatically */
64+ private boolean mFastScrollAlwaysVisible = false ;
65+ /** Type of the view animation (CURRENT_ANIMATION_xxx) */
66+ private int mCurrentAnimationType = CURRENT_ANIMATION_NONE ;
67+ private Runnable mAutoHideProcessRunnable ;
68+
5069 public AbsRecyclerViewFastScroller (Context context ) {
5170 this (context , null , 0 );
5271 }
@@ -83,6 +102,29 @@ public AbsRecyclerViewFastScroller(Context context, AttributeSet attrs, int defS
83102 setOnTouchListener (new FastScrollerTouchListener (this ));
84103 }
85104
105+ @ Override
106+ protected void onAttachedToWindow () {
107+ super .onAttachedToWindow ();
108+
109+ mAutoHideProcessRunnable = new Runnable () {
110+ @ Override
111+ public void run () {
112+ hideWithAnimation ();
113+ }
114+ };
115+ if (!mFastScrollAlwaysVisible ) {
116+ hide ();
117+ }
118+ }
119+
120+ @ Override
121+ protected void onDetachedFromWindow () {
122+ super .onDetachedFromWindow ();
123+
124+ cancelAutoHideScrollerProcess ();
125+ mAutoHideProcessRunnable = null ;
126+ }
127+
86128 private void applyCustomAttributesToView (View view , Drawable drawable , int color ) {
87129 if (drawable != null ) {
88130 setViewBackground (view , drawable );
@@ -183,6 +225,13 @@ public OnScrollListener getOnScrollListener() {
183225 public void onScrolled (RecyclerView recyclerView , int dx , int dy ) {
184226 float scrollProgress = getScrollProgressCalculator ().calculateScrollProgress (recyclerView );
185227 moveHandleToPosition (scrollProgress );
228+
229+ if (!mFastScrollAlwaysVisible ) {
230+ showWithAnimation ();
231+ if (!mIsGrabbingHandle ) {
232+ scheduleAutoHideScrollerProcess ();
233+ }
234+ }
186235 }
187236 };
188237 }
@@ -207,6 +256,162 @@ protected void onLayout(boolean changed, int left, int top, int right, int botto
207256 */
208257 protected abstract void onCreateScrollProgressCalculator ();
209258
259+ /**
260+ * Returns true if the fast scroller is set to always show on this view.
261+ *
262+ * @return true if the fast scroller will always show
263+ */
264+ public boolean isFastScrollAlwaysVisible () {
265+ return mFastScrollAlwaysVisible ;
266+ }
267+
268+ /**
269+ * Set whether or not the fast scroller should always be shown.
270+ *
271+ * @param alwaysVisible true if the fast scroller should always be displayed, false otherwise
272+ */
273+ public void setFastScrollAlwaysVisible (boolean alwaysVisible ) {
274+ if (mFastScrollAlwaysVisible == alwaysVisible ) {
275+ return ;
276+ }
277+ mFastScrollAlwaysVisible = alwaysVisible ;
278+ if (mFastScrollAlwaysVisible ) {
279+ show ();
280+ } else {
281+ cancelAutoHideScrollerProcess ();
282+ }
283+ }
284+
285+ private void scheduleAutoHideScrollerProcess () {
286+ cancelAutoHideScrollerProcess ();
287+
288+ if (!mFastScrollAlwaysVisible ) {
289+ postDelayed (mAutoHideProcessRunnable , AUTO_HIDE_SCROLLER_TIMEOUT_MILLS );
290+ }
291+ }
292+
293+ private void cancelAutoHideScrollerProcess () {
294+ removeCallbacks (mAutoHideProcessRunnable );
295+ }
296+
297+ private void show () {
298+ cancelAutoHideScrollerProcess ();
299+
300+ if (getAnimation () != null ) {
301+ getAnimation ().cancel ();
302+ }
303+
304+ ViewCompat .setTranslationX (this , 0 );
305+ ViewCompat .setTranslationY (this , 0 );
306+
307+ setVisibility (View .VISIBLE );
308+ }
309+
310+ private void hide () {
311+ cancelAutoHideScrollerProcess ();
312+
313+ if (getAnimation () != null ) {
314+ getAnimation ().cancel ();
315+ }
316+ setVisibility (View .INVISIBLE );
317+ }
318+
319+ private void showWithAnimation () {
320+ if ((mCurrentAnimationType == CURRENT_ANIMATION_SHOW ) || (getVisibility () == View .VISIBLE )) {
321+ return ;
322+ }
323+
324+ cancelAutoHideScrollerProcess ();
325+
326+ if (getAnimation () != null ) {
327+ getAnimation ().cancel ();
328+ }
329+
330+ final Animation anim = loadShowAnimation ();
331+
332+ if (anim != null ) {
333+ anim .setAnimationListener (new Animation .AnimationListener () {
334+ @ Override
335+ public void onAnimationStart (Animation animation ) {
336+ setVisibility (View .VISIBLE );
337+ }
338+
339+ @ Override
340+ public void onAnimationEnd (Animation animation ) {
341+ mCurrentAnimationType = CURRENT_ANIMATION_NONE ;
342+ }
343+
344+ @ Override
345+ public void onAnimationRepeat (Animation animation ) {
346+ }
347+ });
348+
349+ mCurrentAnimationType = CURRENT_ANIMATION_SHOW ;
350+
351+ startAnimation (anim );
352+ } else {
353+ show ();
354+ }
355+ }
356+
357+ private void hideWithAnimation () {
358+ if ((mCurrentAnimationType == CURRENT_ANIMATION_HIDE ) || (getVisibility () != View .VISIBLE )) {
359+ return ;
360+ }
361+
362+ cancelAutoHideScrollerProcess ();
363+
364+ if (getAnimation () != null ) {
365+ getAnimation ().cancel ();
366+ }
367+
368+ final Animation anim = loadHideAnimation ();
369+
370+ if (anim != null ) {
371+ anim .setAnimationListener (new Animation .AnimationListener () {
372+ @ Override
373+ public void onAnimationStart (Animation animation ) {
374+ }
375+
376+ @ Override
377+ public void onAnimationEnd (Animation animation ) {
378+ mCurrentAnimationType = CURRENT_ANIMATION_NONE ;
379+ setVisibility (View .INVISIBLE );
380+ }
381+
382+ @ Override
383+ public void onAnimationRepeat (Animation animation ) {
384+ }
385+ });
386+
387+ mCurrentAnimationType = CURRENT_ANIMATION_HIDE ;
388+
389+ startAnimation (anim );
390+ } else {
391+ hide ();
392+ }
393+ }
394+
395+ /**
396+ * Sets whether user is grabbing the scroller handle
397+ * @param isGrabbingHandle true: grabbing, false: not grabbing
398+ */
399+ public void setIsGrabbingHandle (boolean isGrabbingHandle ) {
400+ if (mIsGrabbingHandle == isGrabbingHandle ) {
401+ return ;
402+ }
403+
404+ mIsGrabbingHandle = isGrabbingHandle ;
405+
406+ if (!mFastScrollAlwaysVisible ) {
407+ if (isGrabbingHandle ) {
408+ show ();
409+ } else {
410+ scheduleAutoHideScrollerProcess ();
411+ }
412+ }
413+ }
414+
210415 /**
211416 * Takes a touch event and determines how much scroll progress this translates into
212417 * @param event touch event received by the layout
@@ -235,4 +440,15 @@ public float getScrollProgress(MotionEvent event) {
235440 */
236441 public abstract void moveHandleToPosition (float scrollProgress );
237442
443+ /**
444+ * Loads scroller show animation
445+ * @return animation which is used for the showWithAnimation() method
446+ */
447+ protected abstract Animation loadShowAnimation ();
448+
449+ /**
450+ * Loads scroller hide animation
451+ * @return animation which is used for the hideWithAnimation() method
452+ */
453+ protected abstract Animation loadHideAnimation ();
238454}
0 commit comments