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. 【Java SE】如何用Java实现直接选择排序

    摘要:直接选择排序属于选择排序的一种,但是它的排序算法比冒泡排序的速度要快一些,由于它的算法比较简单,所以也比较适合初学者学习掌握. 适宜人群:有一定Java SE基础,明白Java的数据类型,数组的 ...

  2. Linux 01 Liunx目录结构及文件基本操作

    Linux目录结构及文件基本操作 1.Linux的文件组织目录结构(遵循FHS标准) FHS(Filesystem Hierarchy Standard)标准:多数Linux版本采用这种文件组织形式, ...

  3. 浏览器存储(cookie、localStorage、sessionStorage)

    互联网早期浏览器是没有状态维护,这个就导致一个问题就是服务器不知道浏览器的状态,无法判断是否是同一个浏览器.这样用户登录.购物车功能都无法实现,Lou Montulli在1994年引入到web中最终纳 ...

  4. vue视频学习笔记03

    video 3 git page:任何仓库 master分支,都可以发布(git page)-------------------------------------双向过滤器:Vue.filter( ...

  5. .Net程序员学用Oracle系列(27):PLSQL 之游标、异常和事务

    1.游标 1.1.游标属性 1.2.隐式游标 1.3.游标处理及案例 2.异常 2.1.异常类别 2.2.异常函数 2.3.异常处理及案例 3.事务 3.1.开始事务.结束事务 3.2.自治事务 3. ...

  6. Welcome to my Blog

    Only English? Oh my god!!! why open live write can't write by Chinese?

  7. 受够了if (ModelState.IsValid)?ActionFitlter也是一路的坑啊!

    这篇博客真是干货,干得估计还有点“磕牙”,所以还提供视频和代码.但基础稍弱的同学,怕还是得自行补充一些基础知识——就一篇文章,确实没办法面面俱到. 视频和代码下载:Demo - 百度云盘 · 一起帮 ...

  8. 使用faker 生成中文测试数据

    https://github.com/fzaninotto/Faker/blob/master/src/Faker/Provider/zh_CN/Address.php 常用的类型都在里面. 下面是一 ...

  9. 如何用php实现简单的文件上传功能?(带图解)

    如图所示:点击浏览出现选择文件的对话框,将所选文件上传到保存文件的文件.  关键点:文件上传的图解: 代码: <!DOCTYPE html> <html> <head&g ...

  10. Java源码学习 -- java.lang.StringBuilder,java.lang.StringBuffer,java.lang.AbstractStringBuilder

    一直以来,都是看到网上说“ StringBuilder是线程不安全的,但运行效率高:StringBuffer 是线程安全的,但运行效率低”,然后默默记住:一个是线程安全.一个线程不安全,但对内在原因并 ...