diff --git a/recyclerviewfastscroller/src/main/java/xyz/danoz/recyclerviewfastscroller/AbsRecyclerViewFastScroller.java b/recyclerviewfastscroller/src/main/java/xyz/danoz/recyclerviewfastscroller/AbsRecyclerViewFastScroller.java
index 40d0e18..a3547c8 100644
--- a/recyclerviewfastscroller/src/main/java/xyz/danoz/recyclerviewfastscroller/AbsRecyclerViewFastScroller.java
+++ b/recyclerviewfastscroller/src/main/java/xyz/danoz/recyclerviewfastscroller/AbsRecyclerViewFastScroller.java
@@ -8,12 +8,14 @@
import android.os.Build.VERSION_CODES;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
+import android.support.v4.view.ViewCompat;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.RecyclerView.OnScrollListener;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
+import android.view.animation.Animation;
import android.widget.FrameLayout;
import android.widget.SectionIndexer;
@@ -30,6 +32,14 @@
public abstract class AbsRecyclerViewFastScroller extends FrameLayout implements RecyclerViewScroller {
private static final int[] STYLEABLE = R.styleable.AbsRecyclerViewFastScroller;
+
+ private static final int AUTO_HIDE_SCROLLER_TIMEOUT_MILLS = 1000;
+
+ private static final int CURRENT_ANIMATION_NONE = 0;
+ private static final int CURRENT_ANIMATION_SHOW = 1;
+ private static final int CURRENT_ANIMATION_HIDE = 2;
+
+
/** The long bar along which a handle travels */
protected final View mBar;
/** The handle that signifies the user's progress in the list */
@@ -46,6 +56,15 @@ public abstract class AbsRecyclerViewFastScroller extends FrameLayout implements
* {@link OnScrollListener} an abstract class instead of an interface. Hmmm */
protected OnScrollListener mOnScrollListener;
+ /** true: user is grabbing the handle, false: otherwise */
+ private boolean mIsGrabbingHandle;
+
+ /** true: always show the scroller, false: hide the scroller automatically */
+ private boolean mFastScrollAlwaysVisible = false;
+ /** Type of the view animation (CURRENT_ANIMATION_xxx) */
+ private int mCurrentAnimationType = CURRENT_ANIMATION_NONE;
+ private Runnable mAutoHideProcessRunnable;
+
public AbsRecyclerViewFastScroller(Context context) {
this(context, null, 0);
}
@@ -82,6 +101,29 @@ public AbsRecyclerViewFastScroller(Context context, AttributeSet attrs, int defS
setOnTouchListener(new FastScrollerTouchListener(this));
}
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+
+ mAutoHideProcessRunnable = new Runnable() {
+ @Override
+ public void run() {
+ hideWithAnimation();
+ }
+ };
+ if (!mFastScrollAlwaysVisible) {
+ hide();
+ }
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+
+ cancelAutoHideScrollerProcess();
+ mAutoHideProcessRunnable = null;
+ }
+
private void applyCustomAttributesToView(View view, Drawable drawable, int color) {
if (drawable != null) {
setViewBackground(view, drawable);
@@ -182,6 +224,14 @@ public OnScrollListener getOnScrollListener() {
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
float scrollProgress = getScrollProgressCalculator().calculateScrollProgress(recyclerView);
moveHandleToPosition(scrollProgress);
+
+ // show scroll bar
+ if (!mFastScrollAlwaysVisible) {
+ showWithAnimation();
+ if (!mIsGrabbingHandle) {
+ scheduleAutoHideScrollerProcess();
+ }
+ }
}
};
}
@@ -206,6 +256,162 @@ protected void onLayout(boolean changed, int left, int top, int right, int botto
*/
protected abstract void onCreateScrollProgressCalculator();
+ /**
+ * Returns true if the fast scroller is set to always show on this view.
+ *
+ * @return true if the fast scroller will always show
+ */
+ public boolean isFastScrollAlwaysVisible() {
+ return mFastScrollAlwaysVisible;
+ }
+
+ /**
+ * Sets whether or not the fast scroller should always be shown.
+ *
+ * @param alwaysVisible true if the fast scroller should always be displayed, false otherwise
+ */
+ public void setFastScrollAlwaysVisible(boolean alwaysVisible) {
+ if (mFastScrollAlwaysVisible == alwaysVisible) {
+ return;
+ }
+ mFastScrollAlwaysVisible = alwaysVisible;
+ if (mFastScrollAlwaysVisible) {
+ show();
+ } else {
+ cancelAutoHideScrollerProcess();
+ }
+ }
+
+ private void scheduleAutoHideScrollerProcess() {
+ cancelAutoHideScrollerProcess();
+
+ if (!mFastScrollAlwaysVisible) {
+ postDelayed(mAutoHideProcessRunnable, AUTO_HIDE_SCROLLER_TIMEOUT_MILLS);
+ }
+ }
+
+ private void cancelAutoHideScrollerProcess() {
+ removeCallbacks(mAutoHideProcessRunnable);
+ }
+
+ private void show() {
+ cancelAutoHideScrollerProcess();
+
+ if (getAnimation() != null) {
+ getAnimation().cancel();
+ }
+
+ ViewCompat.setTranslationX(this, 0);
+ ViewCompat.setTranslationY(this, 0);
+
+ setVisibility(View.VISIBLE);
+ }
+
+ private void hide() {
+ cancelAutoHideScrollerProcess();
+
+ if (getAnimation() != null) {
+ getAnimation().cancel();
+ }
+ setVisibility(View.INVISIBLE);
+ }
+
+ private void showWithAnimation() {
+ if ((mCurrentAnimationType == CURRENT_ANIMATION_SHOW) || (getVisibility() == View.VISIBLE)) {
+ return;
+ }
+
+ cancelAutoHideScrollerProcess();
+
+ if (getAnimation() != null) {
+ getAnimation().cancel();
+ }
+
+ final Animation anim = loadShowAnimation();
+
+ if (anim != null) {
+ anim.setAnimationListener(new Animation.AnimationListener() {
+ @Override
+ public void onAnimationStart(Animation animation) {
+ setVisibility(View.VISIBLE);
+ }
+
+ @Override
+ public void onAnimationEnd(Animation animation) {
+ mCurrentAnimationType = CURRENT_ANIMATION_NONE;
+ }
+
+ @Override
+ public void onAnimationRepeat(Animation animation) {
+ }
+ });
+
+ mCurrentAnimationType = CURRENT_ANIMATION_SHOW;
+
+ startAnimation(anim);
+ } else {
+ show();
+ }
+ }
+
+ private void hideWithAnimation() {
+ if ((mCurrentAnimationType == CURRENT_ANIMATION_HIDE) || (getVisibility() != View.VISIBLE)) {
+ return;
+ }
+
+ cancelAutoHideScrollerProcess();
+
+ if (getAnimation() != null) {
+ getAnimation().cancel();
+ }
+
+ final Animation anim = loadHideAnimation();
+
+ if (anim != null) {
+ anim.setAnimationListener(new Animation.AnimationListener() {
+ @Override
+ public void onAnimationStart(Animation animation) {
+ }
+
+ @Override
+ public void onAnimationEnd(Animation animation) {
+ mCurrentAnimationType = CURRENT_ANIMATION_NONE;
+ setVisibility(View.INVISIBLE);
+ }
+
+ @Override
+ public void onAnimationRepeat(Animation animation) {
+ }
+ });
+
+ mCurrentAnimationType = CURRENT_ANIMATION_HIDE;
+
+ startAnimation(anim);
+ } else {
+ hide();
+ }
+ }
+
+ /**
+ * Sets whether user is grabbing the scroller handle
+ * @param isGrabbingHandle true: grabbing, false: not grabbing
+ */
+ public void setIsGrabbingHandle(boolean isGrabbingHandle) {
+ if (mIsGrabbingHandle == isGrabbingHandle) {
+ return;
+ }
+
+ mIsGrabbingHandle = isGrabbingHandle;
+
+ if (!mFastScrollAlwaysVisible) {
+ if (isGrabbingHandle) {
+ show();
+ } else {
+ scheduleAutoHideScrollerProcess();
+ }
+ }
+ }
+
/**
* Takes a touch event and determines how much scroll progress this translates into
* @param event touch event received by the layout
@@ -234,4 +440,15 @@ public float getScrollProgress(MotionEvent event) {
*/
public abstract void moveHandleToPosition(float scrollProgress);
+ /**
+ * Loads scroller show animation
+ * @return animation which is used for the showWithAnimation() method
+ */
+ protected abstract Animation loadShowAnimation();
+
+ /**
+ * Loads scroller hide animation
+ * @return animation which is used for the hideWithAnimation() method
+ */
+ protected abstract Animation loadHideAnimation();
}
\ No newline at end of file
diff --git a/recyclerviewfastscroller/src/main/java/xyz/danoz/recyclerviewfastscroller/FastScrollerTouchListener.java b/recyclerviewfastscroller/src/main/java/xyz/danoz/recyclerviewfastscroller/FastScrollerTouchListener.java
index a4bc5db..33679fb 100644
--- a/recyclerviewfastscroller/src/main/java/xyz/danoz/recyclerviewfastscroller/FastScrollerTouchListener.java
+++ b/recyclerviewfastscroller/src/main/java/xyz/danoz/recyclerviewfastscroller/FastScrollerTouchListener.java
@@ -3,6 +3,7 @@
import xyz.danoz.recyclerviewfastscroller.sectionindicator.SectionIndicator;
import android.support.annotation.Nullable;
+import android.support.v4.view.MotionEventCompat;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
@@ -23,6 +24,17 @@ public FastScrollerTouchListener(AbsRecyclerViewFastScroller fastScroller) {
@Override
public boolean onTouch(View v, MotionEvent event) {
+ int action = MotionEventCompat.getActionMasked(event);
+
+ switch (action) {
+ case MotionEvent.ACTION_DOWN:
+ mFastScroller.setIsGrabbingHandle(true);
+ break;
+ case MotionEvent.ACTION_UP:
+ mFastScroller.setIsGrabbingHandle(false);
+ break;
+ }
+
SectionIndicator sectionIndicator = mFastScroller.getSectionIndicator();
showOrHideIndicator(sectionIndicator, event);
diff --git a/recyclerviewfastscroller/src/main/java/xyz/danoz/recyclerviewfastscroller/vertical/VerticalRecyclerViewFastScroller.java b/recyclerviewfastscroller/src/main/java/xyz/danoz/recyclerviewfastscroller/vertical/VerticalRecyclerViewFastScroller.java
index 3ce61c6..a1f1356 100644
--- a/recyclerviewfastscroller/src/main/java/xyz/danoz/recyclerviewfastscroller/vertical/VerticalRecyclerViewFastScroller.java
+++ b/recyclerviewfastscroller/src/main/java/xyz/danoz/recyclerviewfastscroller/vertical/VerticalRecyclerViewFastScroller.java
@@ -4,6 +4,8 @@
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
import xyz.danoz.recyclerviewfastscroller.R;
import xyz.danoz.recyclerviewfastscroller.AbsRecyclerViewFastScroller;
@@ -50,6 +52,17 @@ public void moveHandleToPosition(float scrollProgress) {
mHandle.setY(mScreenPositionCalculator.getYPositionFromScrollProgress(scrollProgress));
}
+ @Override
+ protected Animation loadShowAnimation() {
+ return AnimationUtils.loadAnimation(getContext(), R.anim.fast_scroller_slide_in_right);
+ }
+
+ @Override
+ protected Animation loadHideAnimation() {
+ return AnimationUtils.loadAnimation(getContext(), R.anim.fast_scroller_slide_out_right);
+ }
+
+ @Override
protected void onCreateScrollProgressCalculator() {
VerticalScrollBoundsProvider boundsProvider =
new VerticalScrollBoundsProvider(mBar.getY(), mBar.getY() + mBar.getHeight() - mHandle.getHeight());
diff --git a/recyclerviewfastscroller/src/main/res/anim/fast_scroller_slide_in_right.xml b/recyclerviewfastscroller/src/main/res/anim/fast_scroller_slide_in_right.xml
new file mode 100644
index 0000000..b0d6d38
--- /dev/null
+++ b/recyclerviewfastscroller/src/main/res/anim/fast_scroller_slide_in_right.xml
@@ -0,0 +1,5 @@
+
+
\ No newline at end of file
diff --git a/recyclerviewfastscroller/src/main/res/anim/fast_scroller_slide_out_right.xml b/recyclerviewfastscroller/src/main/res/anim/fast_scroller_slide_out_right.xml
new file mode 100644
index 0000000..b7ea3cd
--- /dev/null
+++ b/recyclerviewfastscroller/src/main/res/anim/fast_scroller_slide_out_right.xml
@@ -0,0 +1,5 @@
+
+
\ No newline at end of file