Skip to content

Commit

Permalink
Add automatically hide scroller when not scrolling. (default: automat…
Browse files Browse the repository at this point in the history
…ically hide)

Introduced these public methods:

- AbsRecyclerViewFastScroller.isFastScrollAlwaysVisible()
- AbsRecyclerViewFastScroller.setFastScrollAlwaysVisible(boolean alwaysVisible)
  • Loading branch information
h6ah4i committed Feb 20, 2015
1 parent 023f492 commit 8e464f1
Show file tree
Hide file tree
Showing 4 changed files with 205 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -31,6 +33,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 */
Expand All @@ -47,6 +57,12 @@ public abstract class AbsRecyclerViewFastScroller extends FrameLayout implements
* {@link OnScrollListener} an abstract class instead of an interface. Hmmm */
protected OnScrollListener mOnScrollListener;

/** 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);
}
Expand Down Expand Up @@ -83,6 +99,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);
Expand Down Expand Up @@ -183,12 +222,145 @@ public OnScrollListener getOnScrollListener() {
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
float scrollProgress = getScrollProgressCalculator().calculateScrollProgress(recyclerView);
moveHandleToPosition(scrollProgress);

if (!mFastScrollAlwaysVisible) {
showWithAnimation();
scheduleAutoHideScrollerProcess();
}
}
};
}
return mOnScrollListener;
}

/**
* 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;
}

/**
* Set 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() {
if (getAnimation() != null) {
getAnimation().cancel();
}

ViewCompat.setTranslationX(this, 0);
ViewCompat.setTranslationY(this, 0);

setVisibility(View.VISIBLE);
}

private void hide() {
if (getAnimation() != null) {
getAnimation().cancel();
}
setVisibility(View.INVISIBLE);
}

private void showWithAnimation() {
if ((mCurrentAnimationType == CURRENT_ANIMATION_SHOW) || (getVisibility() == View.VISIBLE)) {
return;
}

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;
}

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();
}
}

/**
* Takes a touch event and determines how much scroll progress this translates into
* @param event touch event received by the layout
Expand Down Expand Up @@ -217,4 +389,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();
}
Original file line number Diff line number Diff line change
Expand Up @@ -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 com.example.android.recyclerview.R;
import com.example.recyclerviewfastscroller.ui.scroller.AbsRecyclerViewFastScroller;
Expand Down Expand Up @@ -50,6 +52,16 @@ 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
public void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/accelerate_interpolator"
android:fromXDelta="100%" android:toXDelta="0"
android:duration="@android:integer/config_shortAnimTime"/>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/accelerate_interpolator"
android:fromXDelta="0" android:toXDelta="100%"
android:duration="@android:integer/config_shortAnimTime"/>

0 comments on commit 8e464f1

Please sign in to comment.