RecyclerView scrolled UP/DOWN listener

How do we know if user scrolled down or up in RecyclerView ?

I tried with RecyclerView#OnScrollListener , it gives the amount of vertical scroll and the scroll state. How do we get the last scroll position when started to dragging and scrolled position when scroll state idle.

121482 次浏览

Try this way:

private static int firstVisibleInListview;


firstVisibleInListview = yourLayoutManager.findFirstVisibleItemPosition();

In your scroll listener:

public void onScrolled(RecyclerView recyclerView, int dx, int dy)
{
super.onScrolled(recyclerView, dx, dy);


int currentFirstVisible = yourLayoutManager.findFirstVisibleItemPosition();


if(currentFirstVisible > firstVisibleInListview)
Log.i("RecyclerView scrolled: ", "scroll up!");
else
Log.i("RecyclerView scrolled: ", "scroll down!");


firstVisibleInListview = currentFirstVisible;


}

The accepted answer works fine, but @MaciejPigulski gave more clear and neat solution in the comment below. I just putting it as an answer here. Here's my working code.

mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {


@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if (dy > 0) {
// Scrolling up
} else {
// Scrolling down
}
}


@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);


if (newState == AbsListView.OnScrollListener.SCROLL_STATE_FLING) {
// Do something
} else if (newState == AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
// Do something
} else {
// Do something
}
}
});

I wanted to hide a layout if the recyclerview is scrolled down and then make it visible if the recyclerview is scrolled up. I did some thinking and came up with this logic. Variable y is a global static int. Do not forget to declare y as static int y;

I hope it helps someone :)

 mRecyclerView.addOnScrollListener(new EndlessRecyclerOnScrollListener(lLayout) {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
// super.onScrolled(recyclerView, dx, dy);
y=dy;
}


@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if(mRecyclerView.SCROLL_STATE_DRAGGING==newState){
//fragProductLl.setVisibility(View.GONE);
}
if(mRecyclerView.SCROLL_STATE_IDLE==newState){
// fragProductLl.setVisibility(View.VISIBLE);
if(y<=0){
fragProductLl.setVisibility(View.VISIBLE);
}
else{
y=0;
fragProductLl.setVisibility(View.GONE);
}
}
}
});

Another simple solution that can detect scroll direction with the help of your adapter:

class MyRecyclerViewAdapter extends RecyclerView.Adapter<MyViewHolder> {
int lastItemPosition = -1;


@Override
public void onBindViewHolder(MyViewHolder holder, int position) {
if (position > lastItemPosition) {
// Scrolled Down
}
else {
// Scrolled Up
}
lastItemPosition = position;
}
}

^ Helpful when doing item animations upon scrolling.

PS: This will tell you directions discontinuously until further onBindViewHolder calls.

There is my implementation of CustomRecyclerView with all type of scroll listeners

public class CustomRecyclerView extends RecyclerView
{
private boolean mIsScrolling;
private boolean mIsTouching;
private OnScrollListener mOnScrollListener;
private Runnable mScrollingRunnable;
private int y = 0;


public abstract static class OnScrollListener
{
void onScrollChanged(CustomRecyclerView RvView, int x, int y, int oldX, int oldY)
{
//if you need just override this method
}


void onEndScroll(CustomRecyclerView RvView)
{
//if you need just override this method
}


protected abstract void onGoUp();


protected abstract void onGoDown();
}


public CustomRecyclerView(final Context context)
{
super(context);
}


public CustomRecyclerView(final Context context, @Nullable final AttributeSet attrs)
{
super(context, attrs);
}


public CustomRecyclerView(final Context context, @Nullable final AttributeSet attrs, final int defStyle)
{
super(context, attrs, defStyle);
}


@Override
public boolean dispatchTouchEvent(MotionEvent iEv)
{
if (isEnabled())
{
processEvent(iEv);
super.dispatchTouchEvent(iEv);
return true; //to keep receive event that follow down event
}


return super.dispatchTouchEvent(iEv);
}


private void processEvent(final MotionEvent iEv)
{
switch (iEv.getAction())
{
case MotionEvent.ACTION_DOWN:
y = (int) iEv.getY();
break;


case MotionEvent.ACTION_UP:
y = (int) iEv.getY();


if (mIsTouching && !mIsScrolling && mOnScrollListener != null)
{
mOnScrollListener.onEndScroll(this);
}


mIsTouching = false;
break;
case MotionEvent.ACTION_MOVE:
mIsTouching = true;
mIsScrolling = true;


int newY = (int) iEv.getY();
int difY = y - newY;


int MAX_VALUE = 200;
int MIN_VALUE = -200;
if (difY > MAX_VALUE)
{
if (mOnScrollListener != null)
{
mOnScrollListener.onGoDown();
}
y = newY;
}
else if (difY < MIN_VALUE)
{
if (mOnScrollListener != null)
{
mOnScrollListener.onGoUp();
}
y = newY;
}


break;
}
}


@Override
protected void onScrollChanged(int iX, int iY, int iOldX, int iOldY)
{
super.onScrollChanged(iX, iY, iOldX, iOldY);


if (Math.abs(iOldX - iX) > 0)
{
if (mScrollingRunnable != null)
{
removeCallbacks(mScrollingRunnable);
}


mScrollingRunnable = () ->
{
if (mIsScrolling && !mIsTouching && mOnScrollListener != null)
{
mOnScrollListener.onEndScroll(CustomRecyclerView.this);
}


mIsScrolling = false;
mScrollingRunnable = null;
};


postDelayed(mScrollingRunnable, 200);
}


if (mOnScrollListener != null)
{
mOnScrollListener.onScrollChanged(this, iX, iY, iOldX, iOldY);
}
}


public void scrollToView(final View iV)
{
// Get deepChild Offset
Point childOffset = new Point();
getDeepChildOffset(CustomRecyclerView.this, iV.getParent(), iV, childOffset);
// Scroll to child.


CustomRecyclerView.this.scrollToY(childOffset.y);
}


private void getDeepChildOffset(final ViewGroup mainParent, final ViewParent parent, final View child, final Point accumulatedOffset)
{
ViewGroup parentGroup = (ViewGroup) parent;
accumulatedOffset.x += child.getLeft();
accumulatedOffset.y += child.getTop();
if (parentGroup.equals(mainParent))
{
return;
}
getDeepChildOffset(mainParent, parentGroup.getParent(), parentGroup, accumulatedOffset);
}


public void scrollToY(final int iY)
{
CustomRecyclerView.this.postDelayed(() ->
{
int x = 0;
int y = iY;
ObjectAnimator xTranslate = ObjectAnimator.ofInt(CustomRecyclerView.this, "scrollX", x);
ObjectAnimator yTranslate = ObjectAnimator.ofInt(CustomRecyclerView.this, "scrollY", y);


AnimatorSet animators = new AnimatorSet();
animators.setDuration(500L);
animators.playTogether(xTranslate, yTranslate);
animators.addListener(new Animator.AnimatorListener()
{


@Override
public void onAnimationStart(Animator arg0)
{
// noting
}


@Override
public void onAnimationRepeat(Animator arg0)
{
// noting
}


@Override
public void onAnimationEnd(Animator arg0)
{
// noting
}


@Override
public void onAnimationCancel(Animator arg0)
{
// noting
}
});
animators.start();
}, 300);
}


public void scrollToTop()
{
scrollToY(0);
}


public void setOnRvScrollListener(OnScrollListener mOnEndScrollListener)
{
this.mOnScrollListener = mOnEndScrollListener;
}
}

And there is sample of usage

private void setRecyclerViewSettings()
{
mRv.setLayoutManager(new LinearLayoutManager(getActivity()));
mRv.setOnRvScrollListener(mScrollListener);
}


private CustomRecyclerView.OnScrollListener mScrollListener = new CustomRecyclerView.OnScrollListener()
{
@Override
protected void onGoUp()
{
AppUtils.hideKeyboard(getContext(), mRvDynamicFormQuestions.getFocusedChild());
}


@Override
protected void onGoDown()
{
AppUtils.hideKeyboard(getContext(), mRvDynamicFormQuestions.getFocusedChild());
}
};

this code work for me

private var nearmeListScrollListener = object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)


          

if (recyclerView.scrollState == RecyclerView.SCROLL_STATE_SETTLING)
if (dy > 0) {
layout_vendros_list_header.hide()
layout_vendros_list_header.animate().translationY(1f)
} else if (dy < 0) {
layout_vendros_list_header.show()
layout_vendros_list_header.animate().translationY(0f)
}


}
}
```