写在前面
Google提供了下拉刷新控件,但是并没有提供上拉加载的控件。即使是没有提供,我们一样可以实现。毕竟Android开放性强。目前有很多第三方开源的控件,做的非常优秀,也不外乎上拉加载。也正因为如此,很多人只停留在用轮子,而没思考轮子怎么造成的。用谁都会用,但是又有几个人知道其中实现原理。归回主题,今天打造一个简单的轮子,也可以不是轮子。基于RecyclerView的上拉加载。
思路
上拉,手指向上滑动。加载,最后一条数据的时候显示加载视图,并且加载下一页的数据。
滑动、最后一条数据、显示加载视图、加载数据。也就这四步。
预备
首先写出RecyclerView常用的方式。
RecyclerView rvMain =(RecyclerView) findViewById(R.id.rv_main);
rvMain.setLayoutManager(new LinearLayoutManager(this));
rvMain.setAdapter(adapter = new Adapter(10));
监听滑动
RecyclerView实例下有addOnScrollListener方法,用于监听RecyclerView滑动的回调。实现OnScrollListener类,这里只需要覆盖onScrollStateChanged这方法就好了。
rvMain.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
}
});
最后一条数据
已知道向上滑动,这时就要判断是否最后一条数据。RecyclerView类有一个canScrollVertically方法,表示能否垂直滑动,参数direction表示滑动的方向,1表示往上滑,滑不动就返回false,意味着到底了。-1表示往下滑,滑不动就返回false,意味着到顶了。
boolean canScrollVertically(int direction)// 1往上滑 -1往下滑
加到判断中
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
if (!recyclerView.canScrollVertically(1)) {
Log.e("tag", "向上滑动,显示到最后一个item");
}
}
显示加载视图
前两步都很简单,到了这步也很简单,只是比较繁琐。RecyclerView不负责视图,只能去Adapter搞事情了。
首先创建一个加载视图的ViewHolder类,加载视图你想显示什么样都可以。
class LoadMoreViewHolder extends RecyclerView.ViewHolder
然后在Adapter里面标记两种类型,分别创建不同ViewHolder。如果多type不熟悉,可以先看看RecyclerView的资料。
private static final int TYPE_NORMAL = 1;//显示真正视图的类型
private static final int TYPE_LOADMORE = -1;//显示加载视图的类型
//分别创建ViewHolder
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == TYPE_NORMAL) {
return new NormalViewHolder(LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_normal, parent, false));
}
loadMoreViewHolder = new LoadMoreViewHolder(LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_loadmore, parent, false));//保留下来控制加载视图
return loadMoreViewHolder;
}
//根据位置显示Type
public int getItemViewType(int position) {
if (position == count) {//
return TYPE_NORMAL;
} else {
return TYPE_LOADMORE;
}
}
//因为多出一个LoadMore视图,要记得数量上加多一个
public int getItemCount() {
return count + 1;
}
...
这样加载视图已经加到最后一个item。但是还没解决滑动底部就显示加载视图。
可以在Adapter提供对外的方法,控制LoadMoreViewHolder 中的item显示与隐藏。不能直接隐藏,还要把ItemView的高度和宽度设置为0,不然隐藏item还多出一块空白。
class LoadMoreViewHolder extends RecyclerView.ViewHolder {
private View itemView;
private int height;
private int width;
public LoadMoreViewHolder(View itemView) {
super(itemView);
this.itemView = itemView;
RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) itemView.getLayoutParams();
height = params.height;//保存高度
width = params.width;//保存宽度
}
public void setLoadMore(boolean loadMore) {
RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) itemView.getLayoutParams();
if (loadMore) {
//如果显示,就重新把高宽给回itemView
params.width = width;
params.height = height;
itemView.setVisibility(View.VISIBLE);
} else {
//如果隐藏,就取掉高宽度
params.width = 0;
params.height = 1;//!!!这里要注意了,不能设置0,不然canScrollVertically方法找不到最后一个,永远为true
itemView.setVisibility(View.GONE);
}
itemView.setLayoutParams(params);
}
}
加载数据
到这步基本是完成了。现在来优化一下onScrollStateChanged方法。最好在方法下加个判断canLoadMore 。是否继续加载,数据加载完毕就可以关掉。为了加载的过程中,不能再使用这方法,再加一个isLoading判断。再判断newState是否等于 RecyclerView.SCROLL_STATE_IDLE(滑动停止)。
rvMain.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
if (!recyclerView.canScrollVertically(1) && canLoadMore && !isLoading
&& newState == RecyclerView.SCROLL_STATE_IDLE) {
adapter.setLoadMore(true);
loadData();
}
}
})
private void loadData() {//模拟2秒后获取数据
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
adapter.addCount(10);
adapter.setLoadMore(false);
}
}, 2000);
}
最后
这是最简单的上拉加载!可能会有莫名其妙的BUG,之前遇到一个坑,RecyclerView在CoordinatorLayout嵌套下,RecyclerView的onScrolled方法在只调用一次。这是很坑的,这只能无奈把onScrolled方法内的语法写在onScrollStateChanged。执行效果也是一样。当然你也可以将这些封装起来,当作一个库,使用的时候就方便多了。也可以控制显示不同的加载视图,比如网络失败、请求失败、到底了。。
模糊的演示
源码在这!!
封装之后 github地址 https://github.com/tanxinye/simpleloadmore