我在水平PagerSnapHelper
中使用RecyclerView
来实现类似行为的视图寻呼机。
final PagerSnapHelper pagerSnapHelper = new PagerSnapHelper();
pagerSnapHelper.attachToRecyclerView(recyclerView);
它很好用,但是我希望能够在用户向任一方向更改页面时获得回调。类似于onSwipeLeft
/ onSwipeRight
回调。
我尝试在findTargetSnapPosition
中使用PagerSnapHelper
,但这只给了我targetIndex
,而不是当前索引。我尝试过类似的方法,但实际上并非一直如此。
@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;
}
是否有更好的方法来实现始终有效的目标?谢谢!
答案 0 :(得分:1)
更新
原始类在通知第一次布局时遇到一些问题。现在,它仅在项目位置从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;
}
}
原始答案
由于没有构建方式,因此需要创建自己的自定义类。这个想法是监听RecyclerViews onScrolled()/onScrollStateChanged()
方法,并在需要时通知您的回调。
接口方法有点重叠,我删除了onSwipeLeft/Right()
方法。这只是为了演示。
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);
void onSwipeRight();
void onSwipeLeft();
}
// Properties
private final PagerSnapHelper snapHelper;
private final int type;
private final boolean notifyOnFirstLayout;
private final OnChangeListener listener;
private int snapPosition;
// Constructor
public SnapPagerScrollListener(PagerSnapHelper snapHelper, @Type int type, boolean notifyOnFirstLayout, OnChangeListener listener) {
this.snapHelper = snapHelper;
this.type = type;
this.notifyOnFirstLayout = notifyOnFirstLayout;
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) || notifyOnFirstLayout) {
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 (!isFirstLayout() || notifyOnFirstLayout) {
listener.onSnapped(newSnapPosition);
}
if (!isFirstLayout()) {
if (snapPosition > newSnapPosition) {
listener.onSwipeLeft();
} else if (snapPosition < newSnapPosition) {
listener.onSwipeRight();
}
}
snapPosition = newSnapPosition;
}
}
private boolean isFirstLayout() {
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));
根据{{1}}
如果在布局计算后可见项范围发生更改,则也会调用此回调。在这种情况下,dx和dy将为0。
我添加了额外的布尔值,以在布局计算后的第一时间获取/获取未通知的信息。
RecyclerView.onScrolled()
属性用于定义何时应触发回调。
Type
后通知回调。我使用此模式仅在滚动结束后触发API调用。