Recyclerview Scrolled Up/Down Listener

RecyclerView scrolled UP/DOWN listener

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;

}

Detect start scroll and end scroll in recyclerview

step 1 You can create a class extending RecyclerView.OnScrollListener and override these methods

public class CustomScrollListener extends RecyclerView.OnScrollListener {
public CustomScrollListener() {
}

public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
switch (newState) {
case RecyclerView.SCROLL_STATE_IDLE:
System.out.println("The RecyclerView is not scrolling");
break;
case RecyclerView.SCROLL_STATE_DRAGGING:
System.out.println("Scrolling now");
break;
case RecyclerView.SCROLL_STATE_SETTLING:
System.out.println("Scroll Settling");
break;

}

}

public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
if (dx > 0) {
System.out.println("Scrolled Right");
} else if (dx < 0) {
System.out.println("Scrolled Left");
} else {
System.out.println("No Horizontal Scrolled");
}

if (dy > 0) {
System.out.println("Scrolled Downwards");
} else if (dy < 0) {
System.out.println("Scrolled Upwards");
} else {
System.out.println("No Vertical Scrolled");
}
}
}

step 2- Since setOnScrollListener is deprecated It is better to use addOnScrollListener

 mRecyclerView.addOnScrollListener(new CustomScrollListener());

Detecting the scrolling direction in the adapter (up/down)

This is the easiest and simplest method I came across. And it works like a charm.

view.addOnScrollListener(new View.OnScrollListener() {
@Override
public void onScrolled(@NonNull View view, int dx, int dy) {
if (dy > 0) {
//Scrolling down
} else if (dy < 0) {
//Scrolling up
}
}
});

how to tell if user swipe down or up when recyclerview cannot be scrolled

To solve this problem we create a new TouchInterceptRecyclerView and we use a gesture detector as below.

public class TouchInterceptRecyclerView extends RecyclerView {

private final GestureDetector gestureDetector;

public TouchInterceptRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
this.gestureDetector = new GestureDetector(context, createGestureListener());
}

private GestureDetector.OnGestureListener createGestureListener() {
return new GestureDetector.SimpleOnGestureListener() {

@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
// Based on the 2 motions calculate direction of motion.
// For horizontal scroll we need to find if the scroll direction is
// left or right based on which we add our business logic.
return false;
}
};
}

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
gestureDetector.onTouchEvent(ev);
// This dispatches the event downstream to children.
// We need this to handle things like item click.
return super.dispatchTouchEvent(ev);
}

Now we need to find scroll direction inside #onScroll().
For that we refer the answer here

Now adding that code to calculate direction to our above code, we get:

public class TouchInterceptRecyclerView extends RecyclerView {

private final GestureDetector gestureDetector;

public TouchInterceptRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
this.gestureDetector = new GestureDetector(context, createGestureListener());
}

private GestureDetector.OnGestureListener createGestureListener() {
return new GestureDetector.SimpleOnGestureListener() {

@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
if (e1 == null || e2 == null) {
return false;
}
float x1 = e1.getX();
float y1 = e1.getY();

float x2 = e2.getX();
float y2 = e2.getY();

Direction direction = getDirection(x1, y1, x2, y2);
return onSwipe(direction);
}

private boolean onSwipe(Direction direction) {
if (direction == Direction.left || direction == Direction.right) {
// Handle business logic starting here. <------
Log.d("#########", direction.toString());
}
return false;
}

private Direction getDirection(float x1, float y1, float x2, float y2) {
double angle = getAngle(x1, y1, x2, y2);
return Direction.fromAngle(angle);
}

private double getAngle(float x1, float y1, float x2, float y2) {
double rad = Math.atan2(y1 - y2, x2 - x1) + Math.PI;
return (rad * 180 / Math.PI + 180) % 360;
}
};
}

public enum Direction {
up,
down,
left,
right;

public static Direction fromAngle(double angle) {
if (inRange(angle, 45, 135)) {
return Direction.up;
} else if (inRange(angle, 0, 45) || inRange(angle, 315, 360)) {
return Direction.right;
} else if (inRange(angle, 225, 315)) {
return Direction.down;
} else {
return Direction.left;
}

}

private static boolean inRange(double angle, float init, float end) {
return (angle >= init) && (angle < end);
}
}

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
gestureDetector.onTouchEvent(ev);
// This dispatches the event downstream to children. We need this to handle things like item click.
return super.dispatchTouchEvent(ev);
}
}

Now only thing left is that we only have to use the gesture detector if all items are completely visible on screen.
For that we introduce another method in the TouchInterceptRecyclerView called areAllItemsCompletelyWithinViewPort().
The updated code snippet is below:

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if(areAllItemsCompletelyWithinViewPort()) {
gestureDetector.onTouchEvent(ev);
}
// This dispatches the event downstream to children. We need this to handle things like item click.
return super.dispatchTouchEvent(ev);
}

private boolean areAllItemsCompletelyWithinViewPort() {
if (getAdapter() == null) {
return false;
}
LayoutManager layoutManager = getLayoutManager();
int firstItemPosition = 0;
int lastItemPosition = 0;
if (layoutManager instanceof LinearLayoutManager) {
firstItemPosition = ((LinearLayoutManager) layoutManager).findFirstCompletelyVisibleItemPosition();
lastItemPosition = ((LinearLayoutManager) layoutManager).findLastCompletelyVisibleItemPosition();
}
return firstItemPosition == 0 && lastItemPosition == getAdapter().getItemCount() - 1;
}

I actually tried it with a horizontal scrolling recycler view and it works well. You can easily use it for vertical scrolling one.

How to detect when user have scrolled to the top most item in a RecyclerView

I have solved my question by myself. I used the code below instead

private boolean isUserScrolling = false;
private boolean isListGoingUp = true;

Then in the RecyclerView OnScrollListener

@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
//detect is the topmost item visible and is user scrolling? if true then only execute
if(newState == RecyclerView.SCROLL_STATE_DRAGGING){
isUserScrolling = true;
if(isListGoingUp){
//my recycler view is actually inverted so I have to write this condition instead
if(linearLayoutManager.findLastCompletelyVisibleItemPosition() + 1 == list.size()){
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
if(isListGoingUp) {
if (linearLayoutManager.findLastCompletelyVisibleItemPosition() + 1 == list.size()) {
Toast.makeText(getContext(),"exeute something", Toast.LENGTH_SHORT).show();
}
}
}
},50);
//waiting for 50ms because when scrolling down from top, the variable isListGoingUp is still true until the onScrolled method is executed
}
}
}
}

@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if(isUserScrolling){
if(dy > 0){
//means user finger is moving up but the list is going down
isListGoingUp = false;
}
else{
//means user finger is moving down but the list is going up
isListGoingUp = true;
}
}
}

Recyclerview: Scroll down

I,m assuming you're using a custom adapter with some kind of RecyclerView or so.
Simply create static boolean variable that helps hold true when the bottom is reached in your adapter like below, i'm assuming recyclerView in this case.

public class ChatAdapter extends RecyclerView.Adapter<ChatAdapter.ViewHolder> {

public static boolean bottomReached = false;

@Override //Make sure it happens on bindViewHolder or related...
public void onBindViewHolder(final ViewHolder holder, int position) {
if (position == data.size() - 1)
bottomReached = true;
else
bottomReached = false;

}

}

So in your activity, for example chatActivity, we do like below.

public class ChatActivity extends AppCompatActivity{
ChatAdapter chatAdapter;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chat);

chatAdapter = new ChatAdapter(this, messagesDataSample);
}

private void gotNewMessage(){
if(chatAdapter.bottomReached)
recyclerView.scrollToPosition(adapter.getItemCount() - 1);
else
// else is not necessary as you don't want to do anything.
}

}

Hopefully this helps, else pls let me know what goes wrong.



Related Topics



Leave a reply



Submit