很多人觉得自定义一个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下拉刷新的更多相关文章

  1. 手把手教你实现RecyclerView的下拉刷新和上拉加载更多

    手把手教你实现RecyclerView的下拉刷新和上拉加载更多     版权声明:本文为博主原创文章,遵循CC 4.0 by-sa版权协议,转载请附上原文出处链接和本声明. 本文链接:https:// ...

  2. 手把手教你轻松实现listview上拉加载

    上篇讲了如何简单快速的的实现listview下拉刷新,那么本篇将讲解如何简单快速的实现上拉加载更多.其实,如果你已经理解了下拉刷新的实现过程,那么实现上拉加载更多将变得轻松起来,原理完全一致,甚至实现 ...

  3. Android—自定义控件实现ListView下拉刷新

    这篇博客为大家介绍一个android常见的功能——ListView下拉刷新(参考自他人博客,网址忘记了,阅读他的代码自己理解注释的,希望能帮助到大家): 首先下拉未松手时候手机显示这样的界面: 下面的 ...

  4. ListView下拉刷新、上拉载入更多之封装改进

    在Android中ListView下拉刷新.上拉载入更多示例一文中,Maxwin兄给出的控件比较强大,前面有详细介绍,但是有个不足就是,里面使用了一些资源文件,包括图片,String,layout,这 ...

  5. ListView下拉刷新上拉加载更多实现

    这篇文章将带大家了解listview下拉刷新和上拉加载更多的实现过程,先看效果(注:图片中listview中的阴影可以加上属性android:fadingEdge="none"去掉 ...

  6. listview下拉刷新上拉加载扩展(三)-仿最新版美团外卖

    本篇是基于上篇listview下拉刷新上拉加载扩展(二)-仿美团外卖改造而来,主要调整了headview的布局,并加了两个背景动画,看似高大上,其实很简单: as源码地址:http://downloa ...

  7. listview下拉刷新上拉加载扩展(二)-仿美团外卖

    经过前几篇的listview下拉刷新上拉加载讲解,相信你对其实现机制有了一个深刻的认识了吧,那么这篇文章我们来实现一个高级的listview下拉刷新上拉加载-仿新版美团外卖的袋鼠动画: 项目结构: 是 ...

  8. listview下拉刷新上拉加载扩展(一)

    前两篇实现了listview简单的下拉刷新和上拉加载,功能已经达到,单体验效果稍简陋,那么在这篇文章里我们来加一点效果,已达到我们常见的listview下拉刷新时的效果: 首先,在headview的x ...

  9. Android ListView下拉刷新时卡的问题解决小技巧

    问题:ListView下拉刷新时看上去非常的卡 解决方案: 在BaseAdapter的getView方法中,有三个参数 public View getView(int position, View c ...

随机推荐

  1. class-map与policy-map 配置与qos模版

    将subnet-a 与subnet-b 归入类class1 中.Router1(config)# ip access-list extended subnet-a Router1(config-std ...

  2. 一个页面从输入url到页面加载显示完成,中间都经历了什么

    第一种解释: 一般会经历以下几个过程: 1.首先,在浏览器地址栏中输入url 2.浏览器先查看浏览器缓存-系统缓存-路由器缓存,如果缓存中有,会直接在屏幕中显示页面内容.若没有,则跳到第三步操作. 3 ...

  3. setTimeout、setInterval被遗忘的第三个参数

    一.最近在看promise,惊奇的发现:原来 setTimeout不只有两个参数,我还能说什么呢?赶紧探探究竟. function multiply(input) { return new Promi ...

  4. 3.3 声明[[],]的数组, push_back() 和 back() 的区别

    声明一个 [[],] 的二维数组: vector < vector<int> > res(1, vector<int>()); 或者 vector<vecto ...

  5. OpenCV设置摄像头分辨率及全屏显示

    OpenCV3.0下 设置摄像头分辨率为1920*1440,并全屏显示图像窗口. int _tmain(int argc, _TCHAR* argv[]) { Mat frame; VideoCapt ...

  6. 查找Mysql的安装路径

    在安装Mysql后,却忘记路径了!!!!!!!! 查找的方法:进入Mysql命令行输入,   show variables like "%char%"; 红色部分就是Mysql的安 ...

  7. 【kmp】 剪花布条 HDU - 2087

    [题意概述] [解题思路] 从左往右找,看有几个匹配的字符串,但要注意例如 aaaaaa  aa的输出是3 而不是5,可以这样理解:画布需要减去,一旦减去也就没有了的 [AC] #include< ...

  8. ACM Self Number

    In 1949 the Indian mathematician D.R. Kaprekar discovered a class of numbers called self-numbers. Fo ...

  9. Nginx之(三)Nginx配置

    一个简单的配置文件如下: #定义Nginx运行的用户及用户组 user userName userGroupName; #工作进程数目,根据硬件调整,通常等于CPU数量或者2倍于CPU worker_ ...

  10. hibernate5学习之理解数据库级并发

    本文作者:苏生米沿 本文地址:http://blog.csdn.net/sushengmiyan/article/details/50551741 当我们谈起隔离的时候,我们总是假定两个物体直接要么隔 ...