现在大部分的项目都有这么一个需求,在滑动到列表底部时加载更多,用RecyclerView实现的话基本原理就是利用findLastVisibleItemPosition()方法判断目前视图内的最后一个子项位置是否等于整个recyclerView的最后一个子项,判断的方法在网上流传已经很多了,我在这里贴一下
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
//当前RecyclerView显示出来的最后一个的item的position
int lastPosition = -1;
//当前状态为停止滑动状态SCROLL_STATE_IDLE时
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
if (layoutManager instanceof GridLayoutManager) {
lastPosition = ((GridLayoutManager) layoutManager).findLastVisibleItemPosition();
} else if (layoutManager instanceof LinearLayoutManager) {
lastPosition = ((LinearLayoutManager) layoutManager).findLastVisibleItemPosition();
} else if (layoutManager instanceof StaggeredGridLayoutManager) {
//因为StaggeredGridLayoutManager的特殊性可能导致最后显示的item存在多个,所以这里取到的是一个数组
//得到这个数组后再取到数组中position值最大的那个就是最后显示的position值了
int[] lastPositions = new int[((StaggeredGridLayoutManager) layoutManager).getSpanCount()];
((StaggeredGridLayoutManager) layoutManager).findLastVisibleItemPositions(lastPositions);
lastPosition = findMax(lastPositions);
}
//时判断界面显示的最后item的position是否等于itemCount总数-1也就是最后一个item的position
//如果相等则说明已经滑动到最后了
Log.i("recyclerView", ("lastVisiblePosition" + lastPosition + "ItemCount" + (recyclerView.getLayoutManager().getItemCount() - 1)));
if (lastPosition == recyclerView.getLayoutManager().getItemCount() - 1) {
Toast.makeText(MainActivity.this, "滑到底部了", Toast.LENGTH_SHORT).show();
}
}
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
}
});
很清楚,没有必要解释了,用起来也很舒服
但是当我们在RecyclerView布局外部加上一层NestScrollView 神奇的事情发生了,每次滑动,不管是否已经到达底部,都会显示已经到达底部了。
为什么呢?打印日志发现每次findLastVisibleItemPositions()返回的值都是和getItemCount-1是一样一样的博主当时并没有想到NestScrollView嵌套滑动的问题,于是秉持着大神们一言不合看源码的精神,我撸起袖子开始看源码,单步调试后发现跟没有NestScrollView存在的不同是
/**
* Returns the adapter position of the last visible view. This position does not include
* adapter changes that were dispatched after the last layout pass.
*
* Note that, this value is not affected by layout orientation or item order traversal.
* ({@link #setReverseLayout(boolean)}). Views are sorted by their positions in the adapter,
* not in the layout.
*
* If RecyclerView has item decorators, they will be considered in calculations as well.
*
* LayoutManager may pre-cache some views that are not necessarily visible. Those views
* are ignored in this method.
*
* @return The adapter position of the last visible view or {@link RecyclerView#NO_POSITION} if
* there aren't any visible items.
* @see #findLastCompletelyVisibleItemPosition()
* @see #findFirstVisibleItemPosition()
*/
public int findLastVisibleItemPosition() {
final View child = findOneVisibleChild(getChildCount() - 1, -1, false, true);
return child == null ? NO_POSITION : getPosition(child);
}
/**
* Return the current number of child views attached to the parent RecyclerView.
* This does not include child views that were temporarily detached and/or scrapped.
*
* @return Number of attached children
*/
public int getChildCount() {
return mChildHelper != null ? mChildHelper.getChildCount() : 0;
}
/**
* Returns the number of children that are not hidden.
*
* @return Number of children that are not hidden.
* @see #getChildAt(int)
*/
int getChildCount() {
return mCallback.getChildCount() - mHiddenViews.size();
}
大概意思就是childCount减去隐藏的子项数量,对比发现问题出在Callback的getChildCount方法,这个方法是由RecyclerView重写的,我们看那个方法
@Override
public int getChildCount() {
return RecyclerView.this.getChildCount();
}
/**
* Returns the number of children in the group.
*
* @return a positive integer representing the number of children in
* the group
*/
public int getChildCount() {
return mChildrenCount;
}
NestedScrollView scrollView = (NestedScrollView) findViewById(R.id.main_scroll);
scrollView.setOnScrollChangeListener(new NestedScrollView.OnScrollChangeListener() {
@Override
public void onScrollChange(NestedScrollView v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
if (scrollY > oldScrollY) {
Log.i(TAG, "Scroll DOWN");
}
if (scrollY < oldScrollY) {
Log.i(TAG, "Scroll UP");
}
if (scrollY == 0) {
Log.i(TAG, "TOP SCROLL");
}
if (scrollY == (v.getChildAt(0).getMeasuredHeight() - v.getMeasuredHeight())) {
Log.i(TAG, "BOTTOM SCROLL");
}
}
});