手把手教你轻松实现listview下拉刷新
很多人觉得自定义一个listview下拉刷新上拉加载更多是一件很牛x的事情,不是大神写不出来,我想大多数童鞋都是做项目用到时就百度,什么pulltorefresh,xlistview。。。也不看原理,稍微改造下就用到项目中了,只管用,等再写新项目时又重复上面的步骤,那么今天,我来告诉大家,实现listview下拉刷新是多么简单的一件事情!
先来看下效果图:
首先,我们来确定下下拉刷新的几个状态,如上图:
1.PULL_TO_REFRESH:下拉中(用户刚开始下拉,但headview未全部露出,也就是未达到可刷新状态),文字状态表现>>下拉刷新;
2.RELEASE_TO_REFRESH:准备刷新(只要用户未松手,其实这个状态也属于是下拉中的状态,只是此时已经达到了可刷新的状态,只要用户一松手,就将执行刷新操作),文字状态表现>>松开刷新;
3.REFRESHING:刷新中(执行刷新操作),文字状态表现>>正在刷新;
4.REFRESH_DONE:刷新完成(这个属于初始状态,以及刷新完成后的状态,所以控件及效果都要复原),文字状态表现>>下拉刷新(还原状态);
接下来,我们再看下满足下拉刷新的一些条件及原理:
1.addHeaderView:定义一个headview的layout,并将这个布局添加为headview,当然如果只执行addHeaderView是不行的,因为此时的headview是呈现在大家眼前的,因为它也是listview的一个item,那么我们可以通过设置headview的paddingTop来先把自己给隐藏起来,我们在编辑xml时即可看到paddingTop为正数时以及为负数时的区别,那么此时我们需要将paddingTop设置为-headview的高度,即可将其隐藏起来:
headerViewHeight = headerView.getMeasuredHeight();
headerView.setPadding(0, -headerViewHeight, 0, 0);
2.onTouch:实现下拉刷新必不可少的部分,就是监听onTouch事件,在ACTION_MOVE里根据手指滑动的距离来判断是否达到或未达到下拉刷新的状态,以及根据手指滑动的距离来动态的设置headview的paddingTop以实现headview的高度变化(露出隐藏);
那么如何计算手指滑动的距离呢,很简单,监听ACTION_DOWN记录手指按下时y坐标:
startY = ev.getY();
在ACTION_MOVE里根据ev.getY()-startY 来计算手指移动的距离,首先我们需要了解,屏幕的坐标是从左顶点开始,x轴坐标从左往右逐渐增大,y轴坐标从上往下逐渐增大,那么我们下拉时,y轴的坐标是逐渐增大的,那么ev.getY()-startY必>0,这也将是在ACTION_MOVE里满足可执行下拉刷新操作的一个重要条件;
我们接着再看其它必备条件也就是达到什么条件,才去执行ACTION_MOVE里下拉部分的代码;我们可以想象下,如果listview上滑了很多行,我们再往下滑动时一样满足了ev.getY()-startY>0,但此时并不需要执行下拉刷新,那么我们就需要加一个条件是此时的listview已经滑动到了顶部也就是firstVisibleItem == 0,如何判断呢,监听onScroll即可:
setOnScrollListener(this);
public void onScroll(AbsListView absListView, int firstVisibleItem, int visibleItemCount, int totalItemCount)
另外,如果listview已经在刷新状态REFRESHING,我们再下拉时也是不需要再执行的,那么连起来的这几个条件就是:
if (offsetY > 0 && isScrollFirst && refreshstate != REFRESHING)
好了,满足了这些条件后,我们又将怎么做呢?我们先定义一个刷新状态变量refreshstate,初始时refreshstate=REFRESH_DONE(不解释),那么我们在满足条件后,首先判断:
if(refreshstate==REFRESH_DONE){
refreshstate = PULL_TO_REFRESH;
}
这个容易理解吧,满足条件后,即立马将refreshstate 置为PULL_TO_REFRESH状态,接着判断:
if(refreshstate==PULL_TO_REFRESH){
setSelection(0);
if (headerViewShowHeight - headerViewHeight >= 0) {
refreshstate = RELEASE_TO_REFRESH;
changeHeaderByState(refreshstate);
}
}
setSelection(0)可以防止你在连续上拉下拉时headview的位置变化始终跟随手指,不加的话会出现位移偏差,headerViewShowHeight 即为headview露出部分的高度headerViewShowHeight =offsetY =ev.getY()-startY;
if (headerViewShowHeight - headerViewHeight >= 0)表示手指滑动的距离也就是headview露出的高度>headview的高度时,即达到了可执行刷新操作的状态refreshstate = RELEASE_TO_REFRESH;
changeHeaderByState(refreshstate)方法是根据刷新中的各种状态来改变布局里面的文字显示,动画效果,以及还原各控件状态等:
/**
* 改变headview状态
*
* @param state
*/
private void changeHeaderByState(int state) {
switch (state) {
case REFRESH_DONE:
headerView.setPadding(0, -headerViewHeight, 0, 0);
tv_refresh.setText("下拉刷新");
break;
case RELEASE_TO_REFRESH:
tv_refresh.setText("松开刷新");
break;
case PULL_TO_REFRESH:
tv_refresh.setText("下拉刷新");
break;
case REFRESHING:
headerView.setPadding(0, 0, 0, 0);
tv_refresh.setText("正在刷新");
break;
default:
break;
}
}
再接着判断:
if(refreshstate==RELEASE_TO_REFRESH){
setSelection(0);
if (headerViewShowHeight - headerViewHeight < 0) {
refreshstate = PULL_TO_REFRESH;
changeHeaderByState(refreshstate);
}
}
当此时为RELEASE_TO_REFRESH时,已经可以执行刷新操作了,但是用户此时并未松手释放,又慢慢向上滑了回去,以至于刚headerViewShowHeight - headerViewHeight >= 0又变为了headerViewShowHeight - headerViewHeight<0,那么此时刷新状态就又回到了PULL_TO_REFRESH;
然而除了以上一系列判断,还有最重要的一步,就是动态改变headview的paddingTop啊,要不然headview跟随手指下拉上滑的露出隐藏效果怎么实现啊?
if (refreshstate == PULL_TO_REFRESH || refreshstate == RELEASE_TO_REFRESH) {
headerView.setPadding(0, (int) (headerViewShowHeight - headerViewHeight), 0, 0);
}
好了,手指按下及滑动都处理了,那么还差什么呢,就是手指抬起(ACTION_UP)时的监听:
if (refreshstate == PULL_TO_REFRESH) {
refreshstate = REFRESH_DONE;
changeHeaderByState(refreshstate);
}
if (refreshstate == RELEASE_TO_REFRESH) {
refreshstate = REFRESHING;
changeHeaderByState(refreshstate);
mOnRefreshListener.onRefresh();
}
抬起时,如果状态仍为PULL_TO_REFRESH,那么没达到刷新条件,即将所有控件复原,如果状态为RELEASE_TO_REFRESH,即达到了刷新条件,则将状态refreshstate 置为REFRESHING后,更新headview,并调用刷新接口mOnRefreshListener.onRefresh()(自定义的刷新回调接口);
那么整个onTouch事件:
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
startY = ev.getY();
break;
case MotionEvent.ACTION_MOVE:
offsetY = ev.getY() - startY;
if (offsetY > 0 && isScrollFirst && refreshstate != REFRESHING) {
float headerViewShowHeight = offsetY/REFRESH_RATIO;
switch (refreshstate) {
case REFRESH_DONE:
refreshstate = PULL_TO_REFRESH;
break;
case PULL_TO_REFRESH:
setSelection(0);
if (headerViewShowHeight - headerViewHeight >= 0) {
refreshstate = RELEASE_TO_REFRESH;
changeHeaderByState(refreshstate);
}
break;
case RELEASE_TO_REFRESH:
setSelection(0);
if (headerViewShowHeight - headerViewHeight < 0) {
refreshstate = PULL_TO_REFRESH;
changeHeaderByState(refreshstate);
}
break;
}
if (refreshstate == PULL_TO_REFRESH || refreshstate == RELEASE_TO_REFRESH) {
headerView.setPadding(0, (int) (headerViewShowHeight - headerViewHeight), 0, 0);
}
}
break;
case MotionEvent.ACTION_UP:
if (refreshstate == PULL_TO_REFRESH) {
refreshstate = REFRESH_DONE;
changeHeaderByState(refreshstate);
}
if (refreshstate == RELEASE_TO_REFRESH) {
refreshstate = REFRESHING;
changeHeaderByState(refreshstate);
mOnRefreshListener.onRefresh();
}
break;
}
return super.onTouchEvent(ev);
}
到这里,其实就已经实现了下拉刷新的功能了,是不是很简单,但如果你只用以上代码的话,可能会有点小问题,就是下拉灵敏度太高:
这体验效果也不太好吧,哈哈!解决这个问题也很简单,比如我们想要手指移动高度和headview变化高度为3:1,即我手指滑动3个像素,headview才露出一个像素,那么我们可以声明一个系数变量:
private static final float REFRESH_RATIO = 3.0f;//下拉系数,越大下拉灵敏度越低
offsetY = ev.getY() - startY;
headerViewShowHeight = offsetY/REFRESH_RATIO ;
后面动态改变headview高度的代码:
headerView.setPadding(0, (int) (headerViewShowHeight - headerViewHeight), 0, 0);
也就相应缩小了。
这个例子比较简单,headview中只有文字变化,像我们常用的那种有箭头的,有progress的,甚至有动画放大缩小的,只要掌握了这几种状态变化,就很容易实现了!
另外:因为headview也属于listview的一个item,因此在做item点击事件时一定要排除headview的点击事件,也就是当position==0时不要做任何处理!
看完了这篇文章,你是不是有所启发呢,如果有,我写这篇文章的目的就达成了,如果没有,就多看几遍直到理解为止!
demo下载地址:http://download.csdn.net/detail/baiyuliang2013/9343383
手把手教你轻松实现listview下拉刷新的更多相关文章
- 手把手教你实现RecyclerView的下拉刷新和上拉加载更多
手把手教你实现RecyclerView的下拉刷新和上拉加载更多 版权声明:本文为博主原创文章,遵循CC 4.0 by-sa版权协议,转载请附上原文出处链接和本声明. 本文链接:https:// ...
- 手把手教你轻松实现listview上拉加载
上篇讲了如何简单快速的的实现listview下拉刷新,那么本篇将讲解如何简单快速的实现上拉加载更多.其实,如果你已经理解了下拉刷新的实现过程,那么实现上拉加载更多将变得轻松起来,原理完全一致,甚至实现 ...
- Android—自定义控件实现ListView下拉刷新
这篇博客为大家介绍一个android常见的功能——ListView下拉刷新(参考自他人博客,网址忘记了,阅读他的代码自己理解注释的,希望能帮助到大家): 首先下拉未松手时候手机显示这样的界面: 下面的 ...
- ListView下拉刷新、上拉载入更多之封装改进
在Android中ListView下拉刷新.上拉载入更多示例一文中,Maxwin兄给出的控件比较强大,前面有详细介绍,但是有个不足就是,里面使用了一些资源文件,包括图片,String,layout,这 ...
- ListView下拉刷新上拉加载更多实现
这篇文章将带大家了解listview下拉刷新和上拉加载更多的实现过程,先看效果(注:图片中listview中的阴影可以加上属性android:fadingEdge="none"去掉 ...
- listview下拉刷新上拉加载扩展(三)-仿最新版美团外卖
本篇是基于上篇listview下拉刷新上拉加载扩展(二)-仿美团外卖改造而来,主要调整了headview的布局,并加了两个背景动画,看似高大上,其实很简单: as源码地址:http://downloa ...
- listview下拉刷新上拉加载扩展(二)-仿美团外卖
经过前几篇的listview下拉刷新上拉加载讲解,相信你对其实现机制有了一个深刻的认识了吧,那么这篇文章我们来实现一个高级的listview下拉刷新上拉加载-仿新版美团外卖的袋鼠动画: 项目结构: 是 ...
- listview下拉刷新上拉加载扩展(一)
前两篇实现了listview简单的下拉刷新和上拉加载,功能已经达到,单体验效果稍简陋,那么在这篇文章里我们来加一点效果,已达到我们常见的listview下拉刷新时的效果: 首先,在headview的x ...
- Android ListView下拉刷新时卡的问题解决小技巧
问题:ListView下拉刷新时看上去非常的卡 解决方案: 在BaseAdapter的getView方法中,有三个参数 public View getView(int position, View c ...
随机推荐
- iOS 隐藏导航条分割线
// 导航条分割线 @property (nonatomic, strong) UIView *navSeparateView; // 获取导航条分割线 UIView *backgroundView ...
- Linux 基本bash命令
1.查看文件大小.内存大小.cpu信息.硬盘空间 显示当前目录所有文件大小的命令:ls -lht 内存空间.CPU信息.硬盘空间:htop.top(htop详解参考:http://blog.csdn. ...
- JS文件中获取contextPath的方法
function getContextPath() { var pathName = document.location.pathname; var index = pathName.su ...
- js页面刷新跳转的几种方式及区别
跳转常用方法: window.location.href="index.php"; window.history.back(-1);//类似于按钮,参数是负几,就后退几次. win ...
- C程序练习
1.编程从键盘任意输入两个时间(例如4时55分和1时25分),计算并输出这两个时间之间的间隔.要求不输出时间差的负号. #include<stdio.h> int main() { int ...
- 函数&语法
定义一个函数 加上一些算法,由自己定义的函数,以下是简单的规则: 函数代码块以 def 关键词开头,后接函数标识符名称和圆括号 (). 任何传入参数和自变量必须放在圆括号中间,圆括号之间可以用于定义参 ...
- python笔记十四(高阶函数——map/reduce、filter、sorted)
一.map/reduce 1.map() map(f,iterable),将一个iterable对象一次作用于函数f,并返回一个迭代器. >>> def f(x): #定义一个函数 ...
- Node.js DNS 模块
Node.js DNS 模块用于解析域名.引入 DNS 模块语法格式如下: var dns = require("dns") 方法 序号 方法 & 描述 1 dns.loo ...
- HTML DOM 改变 HTML 内容
HTML DOM 允许 JavaScript 改变 HTML 元素的内容. 改变 HTML 输出流 JavaScript 能够创建动态的 HTML 内容: 今天的日期是: Thu Feb 25 201 ...
- 在Ubuntu12.04上安装图形化配置与window共享的samba服务器
1.安装samba图形化配置界面 sudo apt-get install system-config-samba 2.启动图形化配置界面 3.添加用户,最好是要用adduser命令去添加 具体配置可 ...