前言:
纵观多数App,下拉刷新和上拉加载更多是很常见的功能,但是谷歌官方只有一个SwipeRefreshLayout用来下拉刷新,上拉加载更多还要自己做。
本篇文章基于RecyclerView简单封装了这两个操作,下拉刷新支持LinearLayoutManager、GridLayoutManager和StaggeredGridLayoutManager;上拉加载更多只支持前两者。
概述:
关于此篇文章,你需要提前了解:
1.实际项目开发过程中,能使用到下拉刷新和上拉加载更多功能的往往都和网络请求有关,即,下拉刷新和上拉加载更多需要有后台服务器的支持。
2.不管是下拉刷新还是上拉加载更多,本质上都是通过监听事件的触发而已。
3.如果是单纯的只是使用下拉刷新(不使用上拉加载更多),我们完全可以在每次触发下拉刷新操作时,直接将从后台服务器上请求下来的数据全部通过RecyclerView+Adapter进行展示即可,无须分页,也无须规定当前页展示多少条数据。
//伪代码
.....
mListData.clear();
mListData.addAll(value.getResults());
adapter.notifyDataSetChanged();
.........
但是如果项目中同时集成下拉刷新和上拉加载更多的话,则不管是下拉刷新还是上拉加载更过的接口中必然要涉及一下两个重要的参数:
parm 1:每页请求的数据
parm 2:当前页数
原因很简:如果不设置这两个参数的话,在进行下来刷新时完全可以将所有的数据一起展示,上拉时也就不会触发什么加载更多的操作了。
说在前面:
之前想研究下拉刷新和上拉加载更多的示例代码。从网上找了好多,关于上拉加载更多的逻辑存在各种各样bug,简单有下面几类:
1.简单的模拟,没有后台服务器支持,逻辑走不通。
2.在上拉加载更多的最后的一个item的处理上存在逻辑混乱。虽然尝试做了更改,仍然不能使其正常运行。
3.对数据请求失败的处理不完善。
4.不支持GridLayoutManager对应的多行多列的实现。
好在老天有眼,终于找到一个比较完善的示Demo,如下:
手把手教你实现RecyclerView的下拉刷新和上拉加载更多
其中讲述了下拉刷新和上拉加载更多的使用步骤,以及需要重点关注的逻辑。因此本文就不再赘述其中上述博客已经存在的使用步骤,而是从项目结构的角度帮助大家更好理解和实现对上拉加载更多功能。
效果演示:
上图说话:
说在中间:
在分析项目结构之前呢,先把项目中用到相关知识点或者模式跟大家罗列一下,如果大家对列举的这些内容都已经了解或者已经掌握,则可以继续往下看,如果好多知识你都不曾接触过。那么你继续往下看的话会感到很迷茫,看不懂。(哈哈,有句话说的好,当你感到迷茫的时候,正是促使你学习的最佳时机,不要多想,行动起来吧。)
本项目用到的知识点罗列:1.项目中用到的设计模式是标准的MVP模式(Model-View-Presenter)。 mvp速学通道:MVP模式使用示例详解
2.Rxjava+Retrofi实现网络请求 速学通道: (1).Android Retrofit 2.0 的详细 使用攻略(含实例讲解) (2).彻底搞清楚 RxJava 是什么东西 (3).分分钟使用Retrofit+Rxjava实现网络请求
3.ButterKnife. ButterKnife是一个专注于Android系统View的注入框架,让你从烦人的findViewById中解脱出来。项目中使用的是版本比较老的ButterKnife,所以在获取控件实例时候,的注释字段可能会有些区别,eg.老本本使用的是InjectView,新版本使用的是bindView。这个比较简单。 速学通道:Android Butterknife使用方法总结
好了,上面列举的就是本项目中用到的且会影响到代码研读的知识点,提前先列出来。如果对上面的知识点有了基本的认知或者更深入的理解,才可以进入到下一步。
顺便说一下,上面列举的相关知识点,也是目前进行进行android项目开发时用到的最新最热的知识点。不要感觉这些东西很超前,相反,他们已经出来好几年了。霍霍,是不是意识到了点什么。。。。。
项目结构与分析:
分析:
adapter包:
1.AdapterWrapper.java
/**
* 1. 显示或隐藏"滑动加载更多条目"
* 2. 设置"滑动加载更多条目的状态" 提示加载或正在加载
* 3. 末尾item由wapper添加, 其他item由原生adapter添加
* 4. 隐藏"滑动加载更多条目"时, item数量减1
* 5. 更新操作调用原生adapter的notifyDataSetChanged()
* 6. 不支持StaggeredGridLayoutManager
*/
public class AdapterWrapper extends RecyclerView.Adapter {
..........
...........
2.GankNewsAdapter.java
/**
* "干货"/"福利"的adapter适配器
*/
public class GankNewsAdapter extends RecyclerView.Adapter<GankNewsAdapter.MyViewHolder> {
..........
...........
3.GankViewPagerAdapter.java
/**
* 用于fragment中"android干货"和“福利”tab的切换的适配器
*/
public class GankViewPagerAdapter extends FragmentPagerAdapter {
..........
...........
bean包:
1.GankNewsBean.java
/**
* 用于展示“android干货”的实体类对象
*/
public class GankNewsBean implements Serializable {
/**
* 使用Gson解析json成对象时默认的是将json里对应字段的值解析到java对象里对应字段的属性里面
*/
@SerializedName("desc")
private String mDesc;
@SerializedName("images")
private List<String> mImages;
@SerializedName("publishedAt")
private Date mPublishedAt;
@SerializedName("url")
private String mUrl;
public String getDesc() {
return mDesc;
}
public List<String> getImages() {
return mImages;
}
public Date getPublishedAt() {
return mPublishedAt;
}
public String getUrl() {
return mUrl;
}
}
2.GankResponseBean.java
/**
* 封装的Retrofit返回的数据类
* 使用Gson解析json成对象,默认的是将json里对应字段的值解析到java对象里对应字段的属性里面
*/
public class GankResponseBean implements Serializable {
@SerializedName("results")
private List<GankNewsBean> mResults;
public List<GankNewsBean> getResults() {
return mResults;
}
}
contract包:
1.GankContract.java
/**
* Presenter层接口---控制Model层的数据操作及调用View层的UI操作来完成“中间人”工作
*/
public interface GankContract {
/**
* 用于view(Activity或者Fragment)中进行的ui操作
*/
interface View {
void setPageState(boolean isLoading);
void setListData(List<GankNewsBean> listData);
void onRefreshComplete();
void onLoadMoreComplete();
void onInitLoadFailed();
}
/**
* Presenter是连接model和view的纽带,因此其要提供获取model的接口
*/
interface Presenter {
void onViewCreate();
void onRefresh();
void onLoadMore();
}
}
2.GankPresenter.java
/**
* GankPresenter是连接view和model的纽带,此处职能:通过view的调用去初始化view的相关逻辑,
* 同时调用mode的相关接口去获得数据,并将数据返回给presenter
*/
public class GankPresenter implements GankContract.Presenter {
.............
.............
3.GankURLService.java
/**
* 使用Rxjava和Retrofit封装的数据请求接口
*/
public interface GankURLService {
String BASE_URL = "http://gank.io/api/";
@GET("data/{category}/{pagecount}/{page}")
Observable<GankResponseBean> requestData(
@Path("category") String category,
@Path("pagecount") int countPerPage,
@Path("page") int page);
}
4.NullOnEmptyConverterFactory.java
/**
* 上面两个接口从服务端获取了数据,通过GsonConverterFactory将服务端相应内容解析成对应的实体类。
* 在接口正常响应时(有数据返回),并没有什么异常发生,但当接口请求的数据为空,我们的服务端人员并不是返回理论意义上的空,
* null或者[](数据集合空),而是返回没有响应体body,只有响应头header,content-length为0的Response
*/
public class NullOnEmptyConverterFactory extends Converter.Factory {
@Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
final Converter<ResponseBody, ?> delegate = retrofit.nextResponseBodyConverter(this, type, annotations);
return new Converter<ResponseBody, Object>() {
@Override
public Object convert(ResponseBody body) throws IOException {
if (body.contentLength() == 0) return null;
return delegate.convert(body);
}
};
}
}
好了,主程序代码就不贴出来了,大家通过上面的项目结构先对整体功能结构划分有个了解。然后大家结合本项目的原来出处::::
手把手教你实现RecyclerView的下拉刷新和上拉加载更多
去学习如何应用把。记住多查,多学,多问!
说在最后:
文章最后,我会贴出此项目的示例源码。我在原来示例的基础上,添加了更为详细的注释,同时也对原来示例源码中描述明显有误的地方做了修改。当然了,你也可以直接下载原来博文中的源码示例。重点是要好好分析。
源码中有些比较难理解的地方,可能一时半会理解不过来。没关系,一步步来:
1.先学会如何使用,做到稍加修改就能满足自己的项目需求。
2.在今后再次遇到下拉刷新和上拉加载更多的需求的时候,继续研究。相信随着研究的深入以及相关问题的不断解决。我们一定能完全消化,化为己用。
示例代码下载:
好了,至此完结,同时感谢原示例代码作者,小伙伴如果有疑问请留言,愿我们相互探讨,共同进步。