需求
- 实现功能——分批上拉加载。如分批加载每次30个,第一次显示30个item,上拉到底后,数据再添入30个,一共可显示60个item。
- 上拉加载的控件——RecyclerView。
- 上拉加载的时机——此次最后一个item在屏幕完全可见时(也可以是部分可见)。
实现(滑动刷新数据部分)
- 准备添加下一批数据的方法
/**
* 分批加载 增加此批数据
*/
private void addNextDatas() {
//mPickRecipesList是可显示的所有item列表,有点冗余,但我复用了mTempList,这样是以免我这里数据出错
//mTempList是交给RecyclerView适配器的列表,通过改变这个列表数据,然后局部刷新
if (mPickRecipesList != null) {
int i = 0;
//目前已经注入adapter的个数
int position = mCurrentCount;
//添加下一批数据
while (position < mRecipes.size() && i < mAddCount) {
mPickRecipesList.add(mRecipes.get(position));
position++;
i++;
}
mTempList = mPickRecipesList;
mCurrentCount = mCurrentCount + mAddCount;
}
}
- 准备判断RecyclerView滑到底部的方法(根据最后一个item是否完全可见)
View对象的getLocalVisibleRect(Rect rect)方法
首先Rect对象,是一个记录坐标的对象。有left、right、top、botton四个参数。分别代表
left:View的 左边 离屏幕 左边 的距离;
right:View的 右边 离屏幕 左边 的距离;
top:View的 顶部 离屏幕 顶部 的距离;
bottom:View的 底部 离屏幕 顶部 的距离;
是滴,我没有写错,就是这样的。
而确认RecyclerView中的item是否可见时,它的坐标是以自己为起点。
当使用recyclerView.getChildAt(最后一个item下标),获得最后一个item的View,当它的left=top=0,right=宽,bottom=高时,这个View在屏幕完全可见;当它的left=0,top!=0,right<宽,bottom<高,这个View在屏幕部分可见;
给getLocalVisibleRect(Rect rect)传入new Rect(0, 0, view.getWidth(), view.getHeight()),返回true可见,否则不可见。
/**
* 确定列表目前最后一个完全可见
*
* @param view
* @return
*/
private boolean isOnScreen(View view) {
if (view != null) {
int width = view.getWidth();
int height = view.getHeight();
Rect rect = new Rect(0, 0, width, height);
return view.getLocalVisibleRect(rect);
} else {
MyLog.d(TAG, "isOnScreen view is null");
}
return false;
}
- 使用Handler处理界面刷新
private final Handler mHandler = new Handler(Looper.myLooper()) {
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
MyLog.d(TAG, "message is" + msg.what);
switch (msg.what) {
//mRecipes是我所有要加载的总item列表
//mCurrentCount记录当前所有可显示的item数,也是下一批要放入的数据的开始下标
case SCROLL_IDLE:
//到达当前底部 但数据还没加载完
if (mRecipes != null && mCurrentCount < mRecipes.size()) {
//记录下一批要放入的数据的开始下标,方便局部刷新
int before = mRecipeGirdAdapter.getItemCount();
//增加下一批数据
addNextDatas();
//局部刷新
mRecipeGirdAdapter.notifyItemRangeChanged(before, mRecipeGirdAdapter.getItemCount() - before);
}
break;
}
}
};
- 给对应RecyclerView设置滑动监听事件
滑动时,根据判断最后一个item是否可见,结合滑动状态,判断是否滑到底部。
当非静止状态时候,不进行数据更新,防止边滑动边刷新会卡顿。
/**
* 监听滚动事件
*/
private void initRecyclerView() {
if (mGridView != null) {
mGridView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
//获取目前展示的列表最后一个item的view
boolean visible = isOnScreen(recyclerView.getChildAt(mCurrentCount - 1));
//最后一个item可见
if (visible) {
switch (newState) {
case RecyclerView.SCROLL_STATE_IDLE:
//静止状态 && 加载
if (!mHandler.hasMessages(SCROLL_IDLE)) {
mHandler.sendEmptyMessage(SCROLL_IDLE);
}
break;
case RecyclerView.SCROLL_STATE_DRAGGING:
case RecyclerView.SCROLL_STATE_SETTLING:
//滑动时候到达最底部 不加载
if (mHandler.hasMessages(SCROLL_IDLE)) {
mHandler.removeMessages(SCROLL_IDLE);
}
break;
}
}
}
@Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
}
});
}
}
希望你我日益成长。