更新2
!!重要的提示 !!
如果您以编程方式调用scrollTo()
并使用SnapPagerScrollListener
with ON_SETTLED
,onScrollStateChanged
则不会被调用。所以旧的捕捉位置不会更新。WIP,一旦我修复它就会更新课程。
更新
原始类在通知第一个布局时存在一些问题。现在它仅在项目位置第一次更改RecyclerView.NO_POSITION
为其他位置时触发。
要进一步扩展到仅在用户手势上忽略/触发,因此对 的非编程调用scrollTo()
,请注意在编程调用的情况下onScrolled()
触发dx == 0 and dy == 0
。
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;
}
}
用法:
只需将 SnapPagerScrollListener 的实例添加到您的 RecyclerView
your_recycler_view.addOnScrollListener(new SnapPagerScrollListener(your_snap_helper, SnapPagerScrollListener.ON_SCROLL/ON_SETTLED, true/false, your_on_changed_listener));
该Type
属性用于定义何时触发回调。
- ON_SCROLL:用于在新视图/页面通过中间时立即通知回调
- ON_SETTLED:用于在 RecyclerViews 状态为 后通知回调
SCROLL_STATE_IDLE
。我使用该模式仅在滚动完成后触发 API 调用。