I'm using PagerSnapHelper
in a horizontal RecyclerView
to achieve a view pager like behaviour.
final PagerSnapHelper pagerSnapHelper = new PagerSnapHelper(); pagerSnapHelper.attachToRecyclerView(recyclerView);
It works great, but I want to be able to get callbacks for when the user changes the page in either direction. So something like, onSwipeLeft
/ onSwipeRight
callbacks.
I tried using findTargetSnapPosition
in PagerSnapHelper
, but that only gives me the targetIndex
and not the current index. I tried something like this, but it doesn't really work all the time.
@Override
public int findTargetSnapPosition(RecyclerView.LayoutManager layoutManager, int velocityX, int velocityY) {
final int targetPos = super.findTargetSnapPosition(layoutManager, velocityX, velocityY);
final View currentView = findSnapView(layoutManager);
final int currentPos = layoutManager.getPosition(currentView);
if (currentPos < targetPos) {
callback.onSwipeRight();
} else if (currentPos > targetPos) {
callback.onSwipeLeft();
}
return targetPos;
}
Is there a better way to achieve this which always works? Thanks!
Update 2
!! Important note !!
If you programmatically call scrollTo()
and use the SnapPagerScrollListener
with ON_SETTLED
, onScrollStateChanged
won't get called. So the old snap position does not get updated. WIP, will update the class as soon as I fix it.
Update
The original class had some issues with notifying on first layout. Now it fires only the first time the item position changes from RecyclerView.NO_POSITION
to something else.
To further extend to ignore/fire only on user gestures, so non programmatic calls to scrollTo()
, note that onScrolled()
gets triggered with dx == 0 and dy == 0
in case of a programmatic call.
public class SnapPagerScrollListener extends RecyclerView.OnScrollListener {
// Constants
public static final int ON_SCROLL = 0;
public static final int ON_SETTLED = 1;
@IntDef({ON_SCROLL, ON_SETTLED})
public @interface Type {
}
public interface OnChangeListener {
void onSnapped(int position);
}
// Properties
private final PagerSnapHelper snapHelper;
private final int type;
private final boolean notifyOnInit;
private final OnChangeListener listener;
private int snapPosition;
// Constructor
public SnapPagerScrollListener(PagerSnapHelper snapHelper, @Type int type, boolean notifyOnInit, OnChangeListener listener) {
this.snapHelper = snapHelper;
this.type = type;
this.notifyOnInit = notifyOnInit;
this.listener = listener;
this.snapPosition = RecyclerView.NO_POSITION;
}
// Methods
@Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if ((type == ON_SCROLL) || !hasItemPosition()) {
notifyListenerIfNeeded(getSnapPosition(recyclerView));
}
}
@Override
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (type == ON_SETTLED && newState == RecyclerView.SCROLL_STATE_IDLE) {
notifyListenerIfNeeded(getSnapPosition(recyclerView));
}
}
private int getSnapPosition(RecyclerView recyclerView) {
RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
if (layoutManager == null) {
return RecyclerView.NO_POSITION;
}
View snapView = snapHelper.findSnapView(layoutManager);
if (snapView == null) {
return RecyclerView.NO_POSITION;
}
return layoutManager.getPosition(snapView);
}
private void notifyListenerIfNeeded(int newSnapPosition) {
if (snapPosition != newSnapPosition) {
if (notifyOnInit && !hasItemPosition()) {
listener.onSnapped(newSnapPosition);
} else if (hasItemPosition()) {
listener.onSnapped(newSnapPosition);
}
snapPosition = newSnapPosition;
}
}
private boolean hasItemPosition() {
return snapPosition != RecyclerView.NO_POSITION;
}
}
Usage:
Just add an instance of SnapPagerScrollListener to your RecyclerView
your_recycler_view.addOnScrollListener(new SnapPagerScrollListener(your_snap_helper, SnapPagerScrollListener.ON_SCROLL/ON_SETTLED, true/false, your_on_changed_listener));
The Type
property is for defining when the callbacks should be triggered.
SCROLL_STATE_IDLE
. I use the mode for only firing API calls when the scroll has settled.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.