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
3032public abstract class AbsRecyclerViewFastScroller extends FrameLayout implements RecyclerViewScroller {
3133
3234 private static final int [] STYLEABLE = R .styleable .AbsRecyclerViewFastScroller ;
35+
36+ private static final int AUTO_HIDE_SCROLLER_TIMEOUT_MILLS = 1000 ;
37+
38+ private static final int CURRENT_ANIMATION_NONE = 0 ;
39+ private static final int CURRENT_ANIMATION_SHOW = 1 ;
40+ private static final int CURRENT_ANIMATION_HIDE = 2 ;
41+
42+
3343 /** The long bar along which a handle travels */
3444 protected final View mBar ;
3545 /** The handle that signifies the user's progress in the list */
@@ -46,6 +56,15 @@ public abstract class AbsRecyclerViewFastScroller extends FrameLayout implements
4656 * {@link OnScrollListener} an abstract class instead of an interface. Hmmm */
4757 protected OnScrollListener mOnScrollListener ;
4858
59+ /** true: user is grabbing the handle, false: otherwise */
60+ private boolean mIsGrabbingHandle ;
61+
62+ /** true: always show the scroller, false: hide the scroller automatically */
63+ private boolean mFastScrollAlwaysVisible = false ;
64+ /** Type of the view animation (CURRENT_ANIMATION_xxx) */
65+ private int mCurrentAnimationType = CURRENT_ANIMATION_NONE ;
66+ private Runnable mAutoHideProcessRunnable ;
67+
4968 public AbsRecyclerViewFastScroller (Context context ) {
5069 this (context , null , 0 );
5170 }
@@ -82,6 +101,29 @@ public AbsRecyclerViewFastScroller(Context context, AttributeSet attrs, int defS
82101 setOnTouchListener (new FastScrollerTouchListener (this ));
83102 }
84103
104+ @ Override
105+ protected void onAttachedToWindow () {
106+ super .onAttachedToWindow ();
107+
108+ mAutoHideProcessRunnable = new Runnable () {
109+ @ Override
110+ public void run () {
111+ hideWithAnimation ();
112+ }
113+ };
114+ if (!mFastScrollAlwaysVisible ) {
115+ hide ();
116+ }
117+ }
118+
119+ @ Override
120+ protected void onDetachedFromWindow () {
121+ super .onDetachedFromWindow ();
122+
123+ cancelAutoHideScrollerProcess ();
124+ mAutoHideProcessRunnable = null ;
125+ }
126+
85127 private void applyCustomAttributesToView (View view , Drawable drawable , int color ) {
86128 if (drawable != null ) {
87129 setViewBackground (view , drawable );
@@ -182,6 +224,14 @@ public OnScrollListener getOnScrollListener() {
182224 public void onScrolled (RecyclerView recyclerView , int dx , int dy ) {
183225 float scrollProgress = getScrollProgressCalculator ().calculateScrollProgress (recyclerView );
184226 moveHandleToPosition (scrollProgress );
227+
228+ // show scroll bar
229+ if (!mFastScrollAlwaysVisible ) {
230+ showWithAnimation ();
231+ if (!mIsGrabbingHandle ) {
232+ scheduleAutoHideScrollerProcess ();
233+ }
234+ }
185235 }
186236 };
187237 }
@@ -206,6 +256,162 @@ protected void onLayout(boolean changed, int left, int top, int right, int botto
206256 */
207257 protected abstract void onCreateScrollProgressCalculator ();
208258
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+ * Sets 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+
209415 /**
210416 * Takes a touch event and determines how much scroll progress this translates into
211417 * @param event touch event received by the layout
@@ -234,4 +440,15 @@ public float getScrollProgress(MotionEvent event) {
234440 */
235441 public abstract void moveHandleToPosition (float scrollProgress );
236442
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 ();
237454}
0 commit comments