RecyclerView的滑动检测
RecyclerView的使用中,有时候我们需要监听列表滚动情况:上滑、下滑、顶部、底部。
RecyclerView的滑动检测可以通过给RecyclerView添加滚动监听来实现:recyclerview.addOnScrollListener(RecyclerView.OnScrollListener)
(或recyclerview.setOnScrollListener(RecyclerView.OnScrollListener)
)
所以我们要看一下RecyclerView.OnScrollListener:
/**
* 可添加到 RecyclerView 的滑动监听,用于接收 RecyclerView 的滑动信息。
* @see RecyclerView#addOnScrollListener(OnScrollListener)
*/
public abstract static class OnScrollListener {
/**
*当 RecyclerView的滑动状态改变时回调方法被调用。
*
* @param recyclerView
* @param newState 滚动状态。以下其中一个:
* RecyclerView.SCROLL_STATE_IDLE
* RecyclerView.SCROLL_STATE_DRAGGING
* RecyclerView.SCROLL_STATE_SETTLING
*/
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState){
}
/**
* 当 RecyclerView 滚动时,回调方法被调用。这个方法会在滚动完成后被调用。
* 如果布局计算后可见项发生范围变化(item range changes),也将调用此回调。
* 这种情况下, dx 和 dy 会为 0.
*
* @param recyclerView
* @param dx 水平(horizontal scroll)滚量(距离)
* @param dy 竖直(vertical scroll)滚动量
*/
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy){
}
}
我们需要在 onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy)
方法中判断滑动方向。
其中dx,dy分别表示 在x方向和y方向滑动的值,这个值有正负。
- dy > 0 表示 上滑, dy>0 表示下滑
通过这两个参数就可以监听滑动方向了。可以用Log验证。
RecyclerView还有一个方法 canScrollVertically(int direction)
:direction,负数表示上滑,正数表示下滑。通过判断这个方法的返回值,可知 RecyclerView 滑到顶部还是底部。
public abstract class OnVerticalScrollListener
extends RecyclerView.OnScrollListener {
@Override
public final void onScrolled(RecyclerView recyclerView, int dx, int dy) {
if (!recyclerView.canScrollVertically(-1)) {
onScrolledToBottom(); //滑到底部
} else if (!recyclerView.canScrollVertically(1)) {
onScrolledToTop(); //滑到顶部
} else if (dy > 0) {
onScrolledUp();
} else if (dy < 0) {
onScrolledDown();
}
}
public void onScrolledUp() {
}
public void onScrolledDown() {
}
public void onScrolledToTop() {
}
public void onScrolledToBottom() {
}
}
RecyclerView的滑动位置
几个方法
LinearLayoutManager
findLastVisibleItemPosition()
:最后一个可见位置findFirstVisibleItemPosition()
:第一个可见位置findLastCompletelyVisibleItemPosition()
:最后一个完全可见位置findFirstCompletelyVisibleItemPosition()
:第一个完全可见位置
StaggeredGridLayoutManager
findFirstVisibleItemPositions(int[])
:返回第一个可见span的items的位置findLastVisibleItemPositions(int[])
:返回最后一个可见span的items的位置findFirstCompletelyVisibleItemPositions(int[])
:返回第一个完全可见span的items的位置findLastCompletelyVisibleItemPositions(int[])
:返回最后一个完全可见span的items的位置
使用:
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
if (layoutManager instanceof LinearLayoutManager) {
int lastVisibleItemPosition = ((LinearLayoutManager) layoutManager).findLastVisibleItemPosition();
int firstVisibleItemPosition = ((LinearLayoutManager) layoutManager).findFirstVisibleItemPosition();
int firstCompletelyVisibleItemPosition = ((LinearLayoutManager) layoutManager).findFirstCompletelyVisibleItemPosition();
int lastCompletelyVisibleItemPosition = ((LinearLayoutManager) layoutManager).findLastCompletelyVisibleItemPosition();
} else if (layoutManager instanceof StaggeredGridLayoutManager) {
int[] positions = new int[((StaggeredGridLayoutManager) layoutManager).getSpanCount()];
((StaggeredGridLayoutManager) layoutManager).findLastVisibleItemPositions(positions);
((StaggeredGridLayoutManager) layoutManager).findFirstVisibleItemPositions(positions);
((StaggeredGridLayoutManager) layoutManager).findFirstCompletelyVisibleItemPositions(positions);
((StaggeredGridLayoutManager) layoutManager).findLastCompletelyVisibleItemPositions(positions);
int maxPosition = positions[0];
int minPosition = positions[0];
for (int position : positions) {
maxPosition = Math.max(maxPosition, position);
minPosition = Math.min(minPosition, position);
}
}
}
});
代码示例:
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
int firstCompletelyVisibleItemPosition = layoutManager.findFirstCompletelyVisibleItemPosition();
if (firstCompletelyVisibleItemPosition == 0) {
//滑动到顶部
}
int lastCompletelyVisibleItemPosition = layoutManager.findLastCompletelyVisibleItemPosition();
if (lastCompletelyVisibleItemPosition == layoutManager.getItemCount() - 1) {
//滑动到底部
}
}
});
在滑动到底部可以作分页加载的加载下一页。
记录位置、滑动到记录的位置
记录位置
mLayoutManager.findFirstVisibleItemPosition()
结合 firstVisibleView.getTop()
int findFirstVisibleItemPosition = mLayoutManager.findFirstVisibleItemPosition();
View firstVisibleView = mLayoutManager.findViewByPosition(findFirstVisibleItemPosition);
int offset = firstVisibleView.getTop();
滑到上次记录位置
使用:LayoutManager#scrollToPositionWithOffset(int, int)
,可以比较精确地滑动到记录的具体位置。
mLayoutManager.scrollToPositionWithOffset(firstVisibleItemPosition, offset);
也可以使用 LayoutManager#scrollToPosition(int)
,但是不够精确,只能滑动到某个item的位置。
也可以使用LayoutManager#computeXxx()
系列,也不够准确,而且compute系列貌似主要用于支持滚动条(scrollbar)。