1 public class RefreshListView extends ListView implements OnScrollListener {

 private View mHeaderView;//头布局
private View mFooterView;//脚布局
private ImageView mArrowView;//刷新箭头
private ProgressBar mProgressBar;//进度条
private TextView mTitle;//显示的刷新状态标题
private TextView mLastRefreshTime;//上一次刷新的时间
private RotateAnimation mRotateUpAnim;//向上旋转的动画
private RotateAnimation mRotateDownAnim;//向下旋转的动画
private float startY;//按下的起始y坐标
private int mFirstVisiblePos;//记录第一个可见的item位置
private int mHeaderViewHeight;//头布局测量所得的高度
private int mFooterViewHeight;//脚布局测量所得的高度
private static final int PULL_TO_REFRESH = 0;//下拉刷新
private static final int RELEASE_TO_REFRESH = 1;//释放刷新
private static final int RELEASING = 2;//正在刷新
private int mCurrentState = PULL_TO_REFRESH;//记录当前的刷新状态
private boolean isLoadingMore;//记录上拉加载的状态
private OnRefreshListener mOnRefreshListener;//下拉刷新接口
private OnLoadMoreListener mOnLoadMoreListener;//上拉加载接口 public RefreshListView(Context context) {
super(context);
init(); } public RefreshListView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
} public RefreshListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
} private void initHeaderView() {
//初始化相关布局控件
mHeaderView = View.inflate(getContext(), R.layout.layout_header_list, null);
mArrowView = (ImageView) mHeaderView.findViewById(R.id.iv_arrow);
mProgressBar = (ProgressBar) mHeaderView.findViewById(R.id.pb);
mTitle = (TextView) mHeaderView.findViewById(R.id.tv_title);
mLastRefreshTime = (TextView) mHeaderView.findViewById(R.id.tv_desc_last_refresh);
mLastRefreshTime.setText(getLastRefreshTime());
//提前手动测量,获取mHeaderView的实际高度
mHeaderView.measure(0, 0); //获取测量的高度
mHeaderViewHeight = mHeaderView.getMeasuredHeight();
//通过设置padding隐藏头布局
mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0);
//将头布局增加到ListView中,注意这里必须在setAdapter之前
this.addHeaderView(mHeaderView);
} /**
* 初始化操作
*/
private void init() {
initHeaderView();
initFooterView();
initAnimation();
//设置滑动监听
this.setOnScrollListener(this);
} private void initFooterView() {
//原理和上面添加头布局一样,不做解释了
mFooterView = View.inflate(getContext(), R.layout.layout_footer_list, null);
mFooterView.measure(0, 0);
mFooterViewHeight = mFooterView.getMeasuredHeight();
mFooterView.setPadding(0, -mFooterViewHeight, 0, 0);
this.addFooterView(mFooterView);
} /**
* 初始化下拉刷新的时候,左边箭头的执行动画
*/
private void initAnimation() {
// 向上旋转,围绕自己的中心逆时针旋转180度
mRotateUpAnim = new RotateAnimation(0f, -180f, Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
mRotateUpAnim.setDuration(3000);//设置动画持续的时间
mRotateUpAnim.setFillAfter(true);//设置动画结束停留在结束的位置
// 向下旋转,围绕自己的中心逆时针旋转180度
mRotateDownAnim = new RotateAnimation(-180f, -360f, Animation.RELATIVE_TO_SELF,
0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
mRotateDownAnim.setDuration(3000);
mRotateDownAnim.setFillAfter(true); } /**
* 重写ListView的onTouchEvent方法,处理我们的滑动事件
*/
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
//记录下x坐标
startY = ev.getY();
break;
case MotionEvent.ACTION_MOVE:
//记录下滑动时的y坐标
float endY = ev.getY();
//如果当前已经是正在刷新的状态或者正在加载更多的状态,不做处理
if (mCurrentState == RELEASING || isLoadingMore) {
return super.onTouchEvent(ev);//执行父类的逻辑,我们这边不进行处理
}
//滑动的距离
float dy = endY - startY;
//第一个可见item的position是0.且滑动距离大于0,慢慢显示头布局
if (dy > 0 && mFirstVisiblePos == 0) {
//更新头布局的padding。 topPadding=(-自身高度+滑动的距离)
int paddingTop = (int) (dy - mHeaderViewHeight);
mHeaderView.setPadding(0, paddingTop, 0, 0);
//头布局已经完全显示,并且当前的状态不是释放刷新,切换
if (paddingTop > 0 && mCurrentState != RELEASE_TO_REFRESH) {
mCurrentState = RELEASE_TO_REFRESH;
updateHeaderView();
} else if (paddingTop < 0 && mCurrentState != PULL_TO_REFRESH) {
//头布局没有完全显示,并且现在不是下拉刷新的状态
mCurrentState = PULL_TO_REFRESH;
updateHeaderView(); }
return true;//事件已经被我们消费处理
} break;
case MotionEvent.ACTION_UP:
//抬起,根据当前的状态
if (mCurrentState == PULL_TO_REFRESH) {
//头布局没有完全显示,则恢复原样
mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0);
} else if (mCurrentState == RELEASE_TO_REFRESH) {
//释放刷新,头布局完全显示了
mHeaderView.setPadding(0, 0, 0, 0);
mCurrentState = RELEASING;//正在刷新中
updateHeaderView();
}
break;
default:
break;
}
return super.onTouchEvent(ev);
} /**
* 更新头布局.根据状态值来切换
*/
private void updateHeaderView() {
switch (mCurrentState) {
case PULL_TO_REFRESH://切换成下拉刷新
mTitle.setText("下拉刷新");
mArrowView.startAnimation(mRotateDownAnim);
break;
case RELEASE_TO_REFRESH://切换成释放刷新
mTitle.setText("释放刷新");
mArrowView.startAnimation(mRotateUpAnim);
break;
case RELEASING://切换成正在刷新
mArrowView.clearAnimation();//这里要清除动画
mArrowView.setVisibility(INVISIBLE);
mProgressBar.setVisibility(VISIBLE);
mTitle.setText("正在刷新中");
//回调刷新接口方法进行刷新
if (mOnRefreshListener != null) {
mOnRefreshListener.onRefresh();
}
break;
default:
break;
}
} /**
* 刷新完成
*/
public void onRefreshComplete() {
//还原最初的状态
mCurrentState = PULL_TO_REFRESH;
mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0);
mArrowView.setVisibility(VISIBLE);
mProgressBar.setVisibility(INVISIBLE);
String lastRefreshTime = getLastRefreshTime();
mLastRefreshTime.setText(lastRefreshTime);
} /**
* 上拉加载完成
*/
public void onLoadMoreComplete() {
//还原最初的状态
isLoadingMore = false;
mFooterView.setPadding(0, 0, 0, 0);
} /**
* 获取上一次刷新的时间
*/
private String getLastRefreshTime() {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return format.format(new Date());
} /**
* 对外暴露设置刷新接口的方法
*/
public void setOnRefreshListener(OnRefreshListener mOnRefreshListener) {
this.mOnRefreshListener = mOnRefreshListener;
} public void setOnLoadMoreListener(OnLoadMoreListener mOnLoadMoreListener) {
this.mOnLoadMoreListener = mOnLoadMoreListener;
} /**
* 滑动状态改变
*/
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
if (isLoadingMore || mCurrentState == RELEASING) {
//正在加载更多,或者刷新状态。不做处理
return;
}
if (scrollState == SCROLL_STATE_IDLE && getLastVisiblePosition() == getCount() - 1) {
//空闲状态且到了最后一个item,执行上拉加载
isLoadingMore = true;
mFooterView.setPadding(0, 0, 0, 0); setSelection(getCount() - 1);//跳转到最后一条使其显示加载更多
if (mOnLoadMoreListener != null) {
mOnLoadMoreListener.onLoadMore();
}
} } /**
* 滑动过程
*/
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
int totalItemCount) {
//记录第一个可见item的position
mFirstVisiblePos = firstVisibleItem;
} /**
* 刷新接口
*/
public interface OnRefreshListener { void onRefresh();
} /**
* 加载更多接口
*/
public interface OnLoadMoreListener { void onLoadMore();
}
}

ListView实现下拉刷新和上拉加载功能的更多相关文章

  1. android--------自定义控件ListView实现下拉刷新和上拉加载

    开发项目过程中基本都会用到listView的下拉刷新和上滑加载更多,为了方便重写的ListView来实现下拉刷新,同时添加了上拉自动加载更多的功能. Android下拉刷新可以分为两种情况: 1.获取 ...

  2. listview下拉刷新和上拉加载更多的多种实现方案

    listview经常结合下来刷新和上拉加载更多使用,本文总结了三种常用到的方案分别作出说明. 方案一:添加头布局和脚布局        android系统为listview提供了addfootview ...

  3. Android 使用PullToRefresh实现下拉刷新和上拉加载(ExpandableListView)

    PullToRefresh是一套实现非常好的下拉刷新库,它支持: 1.ListView 2.ExpandableListView 3.GridView 4.WebView 等多种常用的需要刷新的Vie ...

  4. 使用PullToRefresh实现下拉刷新和上拉加载

    使用PullToRefresh实现下拉刷新和上拉加载 分类: Android2013-12-20 15:51 78158人阅读 评论(91) 收藏 举报 Android下拉刷新上拉加载PullToRe ...

  5. 安卓开发笔记——关于开源组件PullToRefresh实现下拉刷新和上拉加载(一分钟搞定,超级简单)

    前言 以前在实现ListView下拉刷新和上拉加载数据的时候都是去继承原生的ListView重写它的一些方法,实现起来非常繁杂,需要我们自己去给ListView定制下拉刷新和上拉加载的布局文件,然后添 ...

  6. Diycode开源项目 搭建可以具有下拉刷新和上拉加载的Fragment

    1.效果预览 1.1.这个首页就是一个Fragment碎片,本文讲述的就是这个碎片的搭建方式. 下拉会有一个旋转的刷新圈,上拉会刷新数据. 1.2.整体结构 首先底层的是BaseFragment 然后 ...

  7. Android 5.X新特性之为RecyclerView添加下拉刷新和上拉加载及SwipeRefreshLayout实现原理

    RecyclerView已经写过两篇文章了,分别是Android 5.X新特性之RecyclerView基本解析及无限复用 和 Android 5.X新特性之为RecyclerView添加Header ...

  8. iscroll.js 下拉刷新和上拉加载

    html代码如下 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> < ...

  9. IOS 开发下拉刷新和上拉加载更多

    IOS 开发下拉刷新和上拉加载更多 简介 1.常用的下拉刷新的实现方式 (1)UIRefreshControl (2)EGOTTableViewrefresh (3)AH3DPullRefresh ( ...

  10. IOS UITableView下拉刷新和上拉加载功能的实现

    在IOS开发中UITableView是非常常用的一个功能,而在使用UITableView的时候我们经常要用到下拉刷新和上拉加载的功能,今天花时间实现了简单的UITableView的下拉刷新和上拉加载功 ...

随机推荐

  1. 通过 U 盘启动重装 macOS 系统

    重装系统是工作和生活中经常需要做的事情,作为一名开发人员,学会该技能你才是一名合格的程序猿!以后再也不会遇到"程旭元你会装系统吗?"的尴尬了!本文主要介绍怎样通过U盘启动重新安装 ...

  2. 关于div+css布局值得注意的地方

    注意项 我们知道,如果想要两个 div(即块级元素)挨着一起排列,可以将其设置为inline-block(行内-块元素). 不过要注意两个div内的内容的对齐方式将是垂直中间对齐,所以这时候就需要使用 ...

  3. hdu1166树状数组

    C国的死对头A国这段时间正在进行军事演习,所以C国间谍头子Derek和他手下Tidy又开始忙乎了.A国在海岸线沿直线布置了N个工兵营地,Derek和Tidy的任务就是要监视这些工兵营地的活动情况.由于 ...

  4. 详解Java动态代理机制

    之前介绍的反射和注解都是Java中的动态特性,还有即将介绍的动态代理也是Java中的一个动态特性.这些动态特性使得我们的程序很灵活.动态代理是面向AOP编程的基础.通过动态代理,我们可以在运行时动态创 ...

  5. List的多维度排序案例演示~

    文章也已同步到我的csdn:http://blog.csdn.net/u012881584/article/details/72377510 关于List的多维度排序. 日常工作中有很多关于list的 ...

  6. PHP. 01. C/S架构、B/S架构、服务器类型、服务器软件、HTTP协议/服务器、数据库、服务器web开发、PHP简介/常见语法、PHPheader()、 PHP_POST/GET数据获取和错误处理

    C/S架构 Client/Server 指客户端,服务器 架构的意思 优点:性能性高:可将一部分的计算工作放在客户端上,服务器只需处理出局即可   洁面炫酷,可使用更多系统提供的效果 缺点:更新软件需 ...

  7. android 本地数据库sqlite的封装

    单机android   sqlite数据库的实现,这个数据库可与程序一起生成在安装包中 一.下载sqlite3.exe文件 二.运行 cmd 转到sqlite3.exe 所在目录  运行 sqlite ...

  8. .net 非对称加密

    后台   public class RSAHelper     {         /// <summary>         /// RSA加密         /// </sum ...

  9. 2017CUIT校赛-线上赛

    2017Pwnhub杯-CUIT校赛 这是CUIT第十三届校赛啦,也是我参加的第一次校赛. 在被虐到崩溃的过程中也学到了一些东西. 这次比赛是从5.27早上十点打到5.28晚上十点,共36小时,中间睡 ...

  10. #417 Div2 B

    #417 Div2 B 题意 给定一个01矩阵表示一幢楼,左右两侧是楼梯,中间是房间,1代表那个房间开灯,0代表关灯,现在某人从1层左端楼梯开始关掉所有灯,当移动某一层时,必须关掉当前层所有灯才能移动 ...