ListView上拉加载和下拉刷新多种实现方式
该篇为ListView下拉刷新和上拉加载实现的各种方法大合集。可能在具体的细节逻辑上处理不太到位,但基本上完成逻辑的实现。细节方面,个人可以根据自己的需求进行完善。
该博客将以四种思路来完成下拉刷新和上拉加载
- 自定义View实现上拉加载和下拉刷新
- 使用PullToRefresh 实现上拉加载和下拉刷新
- 使用Ultra-Pull-To-Refresh实现上拉加载和下拉刷新
- 使用SwipeToRefreshLayout实现上拉加载和下拉刷新
如果你对ListView
不够熟悉,欢迎看我之前的博客 ListView使用总结
自定义View实现上拉加载和下拉刷新
该方法,我们通过完全自定义的方式实现,不添加任何依赖库和jar包。纯原生。
首先看一下我们实现的效果
实现的关键点:
- 为
ListView
添加头布局和底布局。 - 通过改变头布局的
paddingTop
值,来控制控件的显示和隐藏 - 根据我们滑动的状态,动态修改头部布局和底部布局。
看一下代码:
public class CustomRefreshListView extends ListView implements OnScrollListener{
/**
* 头布局
*/
private View headerView;
/**
* 头部布局的高度
*/
private int headerViewHeight;
/**
* 头部旋转的图片
*/
private ImageView iv_arrow;
/**
* 头部下拉刷新时状态的描述
*/
private TextView tv_state;
/**
* 下拉刷新时间的显示控件
*/
private TextView tv_time;
/**
* 底部布局
*/
private View footerView;
/**
* 底部旋转progressbar
*/
private ProgressBar pb_rotate;
/**
* 底部布局的高度
*/
private int footerViewHeight;
/**
* 按下时的Y坐标
*/
private int downY;
private final int PULL_REFRESH = 0;//下拉刷新的状态
private final int RELEASE_REFRESH = 1;//松开刷新的状态
private final int REFRESHING = 2;//正在刷新的状态
/**
* 当前下拉刷新处于的状态
*/
private int currentState = PULL_REFRESH;
/**
* 头部布局在下拉刷新改变时,图标的动画
*/
private RotateAnimation upAnimation,downAnimation;
/**
* 当前是否在加载数据
*/
private boolean isLoadingMore = false;
public CustomRefreshListView(Context context) {
this(context,null);
}
public CustomRefreshListView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init(){
//设置滑动监听
setOnScrollListener(this);
//初始化头布局
initHeaderView();
//初始化头布局中图标的旋转动画
initRotateAnimation();
//初始化为尾布局
initFooterView();
}
/**
* 初始化headerView
*/
private void initHeaderView() {
headerView = View.inflate(getContext(), R.layout.head_custom_listview, null);
iv_arrow = (ImageView) headerView.findViewById(R.id.iv_arrow);
pb_rotate = (ProgressBar) headerView.findViewById(R.id.pb_rotate);
tv_state = (TextView) headerView.findViewById(R.id.tv_state);
tv_time = (TextView) headerView.findViewById(R.id.tv_time);
//测量headView的高度
headerView.measure(0, 0);
//获取高度,并保存
headerViewHeight = headerView.getMeasuredHeight();
//设置paddingTop = -headerViewHeight;这样,该控件被隐藏
headerView.setPadding(0, -headerViewHeight, 0, 0);
//添加头布局
addHeaderView(headerView);
}
/**
* 初始化旋转动画
*/
private void initRotateAnimation() {
upAnimation = new RotateAnimation(0, -180,
RotateAnimation.RELATIVE_TO_SELF, 0.5f,
RotateAnimation.RELATIVE_TO_SELF, 0.5f);
upAnimation.setDuration(300);
upAnimation.setFillAfter(true);
downAnimation = new RotateAnimation(-180, -360,
RotateAnimation.RELATIVE_TO_SELF, 0.5f,
RotateAnimation.RELATIVE_TO_SELF, 0.5f);
downAnimation.setDuration(300);
downAnimation.setFillAfter(true);
}
//初始化底布局,与头布局同理
private void initFooterView() {
footerView = View.inflate(getContext(), R.layout.foot_custom_listview, null);
footerView.measure(0, 0);
footerViewHeight = footerView.getMeasuredHeight();
footerView.setPadding(0, -footerViewHeight, 0, 0);
addFooterView(footerView);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
//获取按下时y坐标
downY = (int) ev.getY();
break;
case MotionEvent.ACTION_MOVE:
if(currentState==REFRESHING){
//如果当前处在滑动状态,则不做处理
break;
}
//手指滑动偏移量
int deltaY = (int) (ev.getY() - downY);
//获取新的padding值
int paddingTop = -headerViewHeight + deltaY;
if(paddingTop>-headerViewHeight && getFirstVisiblePosition()==0){
//向下滑,且处于顶部,设置padding值,该方法实现了顶布局慢慢滑动显现
headerView.setPadding(0, paddingTop, 0, 0);
if(paddingTop>=0 && currentState==PULL_REFRESH){
//从下拉刷新进入松开刷新状态
currentState = RELEASE_REFRESH;
//刷新头布局
refreshHeaderView();
}else if (paddingTop<0 && currentState==RELEASE_REFRESH) {
//进入下拉刷新状态
currentState = PULL_REFRESH;
refreshHeaderView();
}
return true;//拦截TouchMove,不让listview处理该次move事件,会造成listview无法滑动
}
break;
case MotionEvent.ACTION_UP:
if(currentState==PULL_REFRESH){
//仍处于下拉刷新状态,未滑动一定距离,不加载数据,隐藏headView
headerView.setPadding(0, -headerViewHeight, 0, 0);
}else if (currentState==RELEASE_REFRESH) {
//滑倒一定距离,显示无padding值得headcView
headerView.setPadding(0, 0, 0, 0);
//设置状态为刷新
currentState = REFRESHING;
//刷新头部布局
refreshHeaderView();
if(listener!=null){
//接口回调加载数据
listener.onPullRefresh();
}
}
break;
}
return super.onTouchEvent(ev);
}
/**
* 根据currentState来更新headerView
*/
private void refreshHeaderView(){
switch (currentState) {
case PULL_REFRESH:
tv_state.setText(