使用PullToRefresh实现下拉刷新和上拉加载更多的ListView效果如下:
PullToRefresh是非常好用的第三方下拉刷新库,它支持:
1.ListView
2.ExpandableListView
3.GridView
4.WebView
等多种常用的需要刷新的View控件,基本上能够满足我们的开发需要。
下面结合一个小Demo介绍一下使用PullToRefresh实现下拉刷新和上拉加载更多的ListView的方法:
(一) 先到GitHub上下载PullToRefresh的类库,下载地址是:
https://github.com/chrisbanes/Android-PullToRefresh
(二) 解压缩后,导入library到自己的工程目录下,然后让使用此功能的Module与library建立依赖。
library给我们提供了常用控件的实现代码,我们可以参照这些代码来自己开发:
(三) 如何理解下拉刷新和上拉加载更多?
我感觉这段说的很到位:
下拉刷新是重新加载列表,重新请求数据;而上拉加载更多,是在已有列表的基础上,根据当前客户端显示的最后一个item的ID值,找到后续的若干个Item数据,返回给客户端,添加到当前列表的末尾显示的;就发送请求和返回数据的技术而言,没有区别,区别在SQL语句;还有对于Listview是重置列表数据,还是添加数据到列表末尾的问题。
(四)setMode 设置模式:
也可以用 ptr:ptrMode="both"
可选值为:disabled(禁用下拉刷新),pullFromStart(仅支持下拉刷新),pullFromEnd(仅支持上拉刷新),both(二者都支持),manualOnly(只允许手动触发)
如果Mode设置成Mode.BOTH,需要设置刷新Listener为OnRefreshListener2,并实现onPullDownToRefresh()、onPullUpToRefresh()两个方法。
如果Mode设置成Mode.PULL_FROM_START或Mode.PULL_FROM_END,需要设置刷新Listener为OnRefreshListener,同时实现onRefresh()方法。
当然也可以设置为OnRefreshListener2,但是Mode.PULL_FROM_START的时候只调用onPullDownToRefresh()方法,Mode.PULL_FROM的时候只调用onPullUpToRefresh()方法.
如果想上拉、下拉刷新的时候 做一样的操作,那就用OnRefreshListener,上拉下拉的时候都调用
如果想上拉、下拉做不一样的的操作,那就在setOnRefreshListener时 用new OnRefreshListener2
(五) 实现上面效果的代码:
①MainActivity代码:
/**
* 下拉刷新,上拉加载更多
* 上拉加载更多,数据量大需要分页,减少内消耗。避免用户快速向下滑动,减少流量
* 下拉刷新,用户习惯
* 使用第三方 Xlistview pulltorefreshlistview
* 官方(下拉刷新,但是没有上拉加载更多)
* pulltorefreshview其实都是一个自定义的布局,里面包裹着Listview或者其他的控件
*/
public class MainActivity extends AppCompatActivity {
protected PullToRefreshListView mPullToRefreshList;
private MyAdapter mAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.setContentView(R.layout.activity_main);
initView();
//第一次加载数据,默认我下拉刷新
initData(false);
/**
* 设置模式
* DISABLED 不启用刷新功能
* PULL_FROM_START 仅支持下拉
* PULL_FROM_END 仅支持上拉
* BOTH 支持上拉和下拉
* MANUAL_REFRESH_ONLY 不支持手势刷新,但是支持使用点击或者其他动作通过代码手动调用刷新
*
*/
mPullToRefreshList.setMode(PullToRefreshBase.Mode.BOTH);
//如果mode为both时(即有两种状态),使用这个监听
mPullToRefreshList.setOnRefreshListener(new PullToRefreshBase.OnRefreshListener2() {
//开始下拉 我们需要在用户下拉的时候重新做http请求
@Override
public void onPullDownToRefresh(PullToRefreshBase refreshView) {
initData(false);
}
@Override
public void onPullUpToRefresh(PullToRefreshBase refreshView) {
initData(true);
}
});
mAdapter = new MyAdapter();
mPullToRefreshList.setAdapter(mAdapter);
//如果需要自定义头标题,先拿到原生控件,在原生控件的基础上添加头标题
//ListView refreshableView = mPullToRefreshList.getRefreshableView();
//refreshableView.addHeaderView(null);
//获取一个头部和尾部的布局
ILoadingLayout loadingLayoutProxy = mPullToRefreshList.getLoadingLayoutProxy();
loadingLayoutProxy.setRefreshingLabel("正在刷新..."); // 刷新时
loadingLayoutProxy.setPullLabel("下拉刷新"); // 刚下拉时,显示的提示
loadingLayoutProxy.setLoadingDrawable(getResources().getDrawable(R.mipmap.ic_launcher));
loadingLayoutProxy.setLastUpdatedLabel("刚刚"); //一般是上次刷新的时间
loadingLayoutProxy.setReleaseLabel("松手开始刷新");
}
private void initView() {
mPullToRefreshList = (PullToRefreshListView) findViewById(R.id.pullToRefresh);
}
//要获取实体类中的数据
private List mResult = new ArrayList<>();
/**
* 获取数据,参数为判断是上拉加载更多,还是下拉刷新
*/
private void initData(final boolean isUP) {
//使用xutils获取网络数据
HttpUtils httpUtils = new HttpUtils();
String url = "http://api.shigeten.net/api/Critic/GetCriticList";
httpUtils.send(HttpRequest.HttpMethod.GET, url, new RequestCallBack() {
@Override
public void onSuccess(ResponseInfo responseInfo) {
//使用Gson解析之前需导包
Gson gson = new Gson();
ListData listData = gson.fromJson(responseInfo.result, ListData.class);
List result = listData.getResult();
if (isUP) { //上拉加载更多
mResult.addAll(result);
mAdapter.setResults(mResult);
mAdapter.notifyDataSetChanged();
} else { //下拉刷新
mAdapter.setResults(result);
mAdapter.notifyDataSetChanged();
}
//刷新完成提供给我们手动调用
mPullToRefreshList.onRefreshComplete();
Toast.makeText(MainActivity.this, "数据请求成功", Toast.LENGTH_SHORT).show();
}
@Override
public void onFailure(HttpException error, String msg) {
Log.e("onFailure", error.getMessage());
}
});
}
}
注:上面的代码用到了第三方框架xUtils和和Gson,需要提前把这两个包导入。
②对应的自定义适配器代码:
public class MyAdapter extends BaseAdapter {
List results;
public void setResults(List results) {
this.results = results;
}
@Override
public int getCount() {
return results == null ? 0 : results.size();
}
@Override
public Object getItem(int position) {
return results.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null){
convertView = View.inflate(parent.getContext(),R.layout.item,null);
holder = new ViewHolder();
holder.iv = (ImageView) convertView.findViewById(R.id.img);
holder.tv = (TextView) convertView.findViewById(R.id.text);
convertView.setTag(holder);
}else{
holder = (ViewHolder) convertView.getTag();
}
ListData.ResultEntity resultEntity = results.get(position);
holder.tv.setText(resultEntity.getTitle());
BitmapUtils bitmapUtils = new BitmapUtils(parent.getContext());
bitmapUtils.display(holder.iv, "http://api.shigeten.net/" + resultEntity.getImage());
return convertView;
}
class ViewHolder{
private TextView tv;
private ImageView iv;
}
}
public class ListData {
/*
* status : 0
* errMsg : null
*/
private int status;
private Object errMsg;
/**
* id : 100045
* type : 1
* publishtime : 636107040000000000
* title : 《瑞士军刀男》人生已经如此艰难,就不要拆穿
* summary : 看似荒诞,实则厚重。生与死的距离其实真的不远,自我认知与自我救赎也只是一线之隔。不知道有没有人和我一样,看到最后尸体踏浪而去的时候内心澎湃不已。
* image : images/B0181F9BC6EFC33D0396A16556952710.jpg
*/
private List result;
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public Object getErrMsg() {
return errMsg;
}
public void setErrMsg(Object errMsg) {
this.errMsg = errMsg;
}
public List getResult() {
return result;
}
public void setResult(List result) {
this.result = result;
}
public static class ResultEntity {
private int id;
private int type;
private long publishtime;
private String title;
private String summary;
private String image;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
public long getPublishtime() {
return publishtime;
}
public void setPublishtime(long publishtime) {
this.publishtime = publishtime;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getSummary() {
return summary;
}
public void setSummary(String summary) {
this.summary = summary;
}
public String getImage() {
return image;
}
public void setImage(String image) {
this.image = image;
}
}
}
④主布局代码activity_main.xml: