ListView的下拉刷新和上拉加载
Posted 爱coding的卖油翁
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ListView的下拉刷新和上拉加载相关的知识,希望对你有一定的参考价值。
我们公司以前有道面试题,让开发人员当场写一个ListView的下拉刷新和上拉加载,时间2个小时左右,允许有微量Bug。
自己想一想,也不知道能不能写的出来,所以今天有时间,参考了网上的一些资料,先写一个练手下。先看下效果图:
实现原理
ListView有一个addHeaderView()和addFooterView()方法,就是添加一个头布局和一个脚布局。主要通过这两个方法来实现。
初始化的时候,添加头布局和脚布局。然后把头布局的paddingTop设置为负的头布局高度,这样头布局就会看不见了。脚布局同样处理。
在手指滑动的时候,不断的去改变paddingTop的值,这样,就形成下拉的效果了。
注意:对于ListView判断滑动到顶部和底部的监听
ListView自带一个setOnScrollListener()监听器,主要用来监听它的滑动时间的。
@Override
public void onScrollStateChanged(AbsListView view, int scrollState)
switch (scrollState)
case OnScrollListener.SCROLL_STATE_IDLE:
//空闲状态,停止滚动
break;
case OnScrollListener.SCROLL_STATE_FLING:
// 手指快速滚动
break;
case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:
// 正在滚动
break;
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount)
调用方法
- 直接引用PullToRefreshListView类到你的工程,copy布局文件。
- 由于下拉刷新的头布局和上拉加载的布局是固定的,没有提供接口动态加载。所有如果要引用的话,得去修改xml文件。
- 可以禁止下拉刷新或者上拉加载,通过setNeedPullToRefresh()和setNeedLoadMore()方法。
- 每次刷新完毕,请调用refreshComplete()方法。
- setOnRefreshListener()设置下拉和上拉的监听回调。
package com.geek.widget;
import java.text.SimpleDateFormat;
import java.util.Locale;
import com.zhou.customviewone.R;
import android.annotation.SuppressLint;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
/**
*
* 下拉刷新和上拉加载的ListView
*
* @author LeeShenzhou
*
*/
public class PullToRefreshListView extends ListView implements OnScrollListener
// 头布局
private View mHeaderView;
private ImageView mImageArrow;
private ProgressBar mProgressBar;
private TextView mTxtState;
private TextView mTxtUpdateTime;
// 脚布局
private View mFooterView;
// 头布局的高度
private int headerHeight;
// 脚布局的高度
private int footerHeight;
private int mDownY;
// 下拉刷新
private final int DOWN_REFRESH = 0;
// 松开刷新
private final int RELEASE_REFRESH = 1;
// 正在刷新中
private final int REFRESHING = 2;
private int currentState = DOWN_REFRESH;
private boolean isLoadMore;
// 是否需要下拉刷新
private boolean isNeedPullToRefresh;
// 是否需要上拉加载更多
private boolean isNeedLoadMore;
/**
* 是否需要下拉刷新
*/
public void setNeedPullToRefresh(boolean need)
this.isNeedPullToRefresh = need;
/**
* 是否需要上拉加载更多
*/
public void setNeedLoadMore(boolean need)
this.isNeedLoadMore = need;
/**
* 刷新完成
*/
public void refreshComplete()
isLoadMore = false;
currentState = DOWN_REFRESH;
mHeaderView.setPadding(0, -headerHeight, 0, 0);
mFooterView.setPadding(0, -footerHeight, 0, 0);
private OnRefreshListener listener;
public void setOnRefreshListener(OnRefreshListener listener)
this.listener = listener;
public PullToRefreshListView(Context context)
super(context);
init();
public PullToRefreshListView(Context context, AttributeSet attrs)
super(context, attrs);
init();
public PullToRefreshListView(Context context, AttributeSet attrs, int defStyle)
super(context, attrs, defStyle);
init();
private void init()
isNeedLoadMore = true;
isNeedPullToRefresh = true;
isLoadMore = false;
// 初始化头布局
mHeaderView = View.inflate(getContext(), R.layout.layout_pull_to_refresh_header, null);
mImageArrow = (ImageView) mHeaderView.findViewById(R.id.header_image_arrow);
mProgressBar = (ProgressBar) mHeaderView.findViewById(R.id.header_pb);
mTxtState = (TextView) mHeaderView.findViewById(R.id.header_state);
mTxtUpdateTime = (TextView) mHeaderView.findViewById(R.id.header_update_time);
mHeaderView.measure(0, 0); // 系统会帮我们测量出HeaderView的高度
headerHeight = mHeaderView.getMeasuredHeight();
mHeaderView.setPadding(0, -headerHeight, 0, 0);
addHeaderView(mHeaderView);
// 初始化脚布局
mFooterView = View.inflate(getContext(), R.layout.layout_pull_to_refresh_footer, null);
mFooterView.measure(0, 0);
footerHeight = mFooterView.getMeasuredHeight();
mFooterView.setPadding(0, -footerHeight, 0, 0);
addFooterView(mFooterView);
// 设置最后刷新时间
setLastUpdateTime();
setOnScrollListener(this);
private void setLastUpdateTime()
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm", Locale.getDefault());
String time = sdf.format(System.currentTimeMillis());
if (mTxtUpdateTime != null)
mTxtUpdateTime.setText("最新更新: " + time);
@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouchEvent(MotionEvent ev)
if (!isNeedPullToRefresh)
return super.onTouchEvent(ev);
switch (ev.getAction())
case MotionEvent.ACTION_DOWN:
mDownY = (int) ev.getY();
break;
case MotionEvent.ACTION_MOVE:
int moveY = (int) ev.getY();
final int offset = (moveY - mDownY) / 2;
int paddingTop = -headerHeight + offset;
if (getFirstVisiblePosition() == 0 && paddingTop > -headerHeight)
if (currentState != REFRESHING)
if (offset > headerHeight && currentState == DOWN_REFRESH)
// 完全显示了
currentState = RELEASE_REFRESH;
changeHeaderView();
else if (offset < headerHeight && currentState == RELEASE_REFRESH)
// 没有显示完全
currentState = DOWN_REFRESH;
changeHeaderView();
if (currentState == REFRESHING)
mHeaderView.setPadding(0, offset, 0, 0);
else
mHeaderView.setPadding(0, paddingTop, 0, 0);
break;
case MotionEvent.ACTION_UP:
if (currentState == REFRESHING)
mHeaderView.setPadding(0, 0, 0, 0);
setSelection(0);
return super.onTouchEvent(ev);
// 判断当前的状态是松开刷新还是下拉刷新
if (currentState == RELEASE_REFRESH)
mHeaderView.setPadding(0, 0, 0, 0);
currentState = REFRESHING;
changeHeaderView();
setSelection(0);
if (listener != null)
listener.onPullToRefresh();
else if (currentState == DOWN_REFRESH)
mHeaderView.setPadding(0, -headerHeight, 0, 0);
break;
default:
break;
return super.onTouchEvent(ev);
private void changeHeaderView()
switch (currentState)
case DOWN_REFRESH:
// 下拉刷新
mTxtState.setText("下拉刷新");
mProgressBar.setVisibility(View.GONE);
mImageArrow.setVisibility(View.VISIBLE);
mImageArrow.setImageResource(R.drawable.arrow_down);
break;
case RELEASE_REFRESH:
// 松开刷新
mTxtState.setText("释放立即刷新");
mProgressBar.setVisibility(View.GONE);
mImageArrow.setVisibility(View.VISIBLE);
mImageArrow.setImageResource(R.drawable.arrow_pull);
break;
case REFRESHING:
// 正在刷新
mImageArrow.setVisibility(View.GONE);
mProgressBar.setVisibility(View.VISIBLE);
mTxtState.setText("正在刷新...");
break;
default:
break;
@Override
public void onScrollStateChanged(AbsListView view, int scrollState)
if (!isNeedLoadMore || isLoadMore)
return;
if (scrollState == SCROLL_STATE_IDLE || scrollState == SCROLL_STATE_FLING)
// 判断当前是否已经到了底部
if (getLastVisiblePosition() == (getCount() - 1))
isLoadMore = true;
mFooterView.setPadding(0, 0, 0, 0);
setSelection(getCount());
if (listener != null)
listener.onLoadMore();
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount)
if (firstVisibleItem != 0)
// 重置刷新状态
if (currentState != REFRESHING)
currentState = DOWN_REFRESH;
public interface OnRefreshListener
/**
* 下拉刷新
*/
void onPullToRefresh();
/**
* 上拉加载更多
*/
void onLoadMore();
源码下载:https://git.oschina.net/nszKnife/AndroidView
以上是关于ListView的下拉刷新和上拉加载的主要内容,如果未能解决你的问题,请参考以下文章
Android实现RecyclerView的下拉刷新和上拉加载更多
vue2.0 自定义 下拉刷新和上拉加载更多(Scroller) 组件