Android学习之——ListView下拉刷新

背景知识

ListView使用非常广泛,对于使用ListView的应用来说,下拉刷新是必不可少要实现的功能。

我们常用的微博、网易新闻,搜狐新闻都使用了这一功能,如下图所示。

    微博

搜狐新闻


具体学习:

首先分析下拉刷新的具体操作过程:

用户手指在ListView上按下往下拉----->出现一个提示的View在ListView顶部----->ListView内容更新,顶部的提示View消失

   具体实现步骤:

   1.创建继承自ListView的RefreshListView,并添加顶部提示View

   

public class RefreshListView extends ListView {
    View header;// 顶部提示View
    public RefreshListView(Context context) {
        super(context);
        initView(context);
    }

    public RefreshListView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView(context);
    }

    public RefreshListView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView(context);
    }

    private void initView(Context context) {
        // LayoutInflater作用是加载布局
        LayoutInflater inflater = LayoutInflater.from(context);
        header = inflater.inflate(R.layout.header_layout, null);
        this.addHeaderView(header);
    }
}

顶部提示View布局文件 header_layout.xml:

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 3     android:layout_width="match_parent"
 4     android:layout_height="match_parent"
 5     android:orientation="vertical" >
 6
 7    <RelativeLayout
 8        android:layout_width="match_parent"
 9        android:layout_height="wrap_content"
10        android:paddingTop="10dip"
11        android:paddingBottom="10dip"
12        >
13        <LinearLayout
14            android:layout_width="wrap_content"
15            android:layout_height="wrap_content"
16            android:orientation="vertical"
17            android:id="@+id/layout"
18            android:layout_centerInParent="true"
19            android:gravity="center"
20            >
21            <!-- 提示文字 -->
22            <TextView
23                android:id="@+id/tips"
24                android:layout_width="wrap_content"
25                android:layout_height="wrap_content"
26                android:text="下拉可以刷新"
27                />
28            <!-- 距上次更新至今的时间 -->
29            <TextView
30                android:id="@+id/time"
31                android:layout_width="wrap_content"
32                android:layout_height="wrap_content"
33                />
34
35        </LinearLayout>
36        <!-- 箭头 -->
37        <ImageView
38            android:id="@+id/arrow"
39            android:layout_width="wrap_content"
40            android:layout_height="wrap_content"
41            android:layout_toLeftOf="@id/layout"
42            android:src="@drawable/pull_to_refresh_arrow"
43            />
44        <!-- 更新进度条 -->
45        <ProgressBar
46            android:id="@+id/progress"
47            android:layout_width="wrap_content"
48            android:layout_height="wrap_content"
49            style="?android:attr/progressBarStyleSmall"
50            android:layout_toLeftOf="@id/layout"
51            android:visibility="gone"
52            />
53    </RelativeLayout>
54 </LinearLayout>

运行效果图:

2.由上图可以知道,默认加载header是显示出来的。默认我们应该隐藏顶部提示布局header。

 1 private void initView(Context context){
 2         //LayoutInflater作用是加载布局
 3         LayoutInflater inflater = LayoutInflater.from(context);
 4         header = inflater.inflate(R.layout.header_layout, null);
 5         measureView(header);
 6         headerHeight = header.getMeasuredHeight();
 7         topPadding(-headerHeight);
 8         this.addHeaderView(header);
 9     }
10     /**
11      * 设置顶部布局的上边距
12      * @param topPadding
13      */
14     private void topPadding(int topPadding){
15         //设置顶部提示的边距
16         //除了顶部用参数值topPadding外,其他三个用header默认的值
17         header.setPadding(header.getPaddingLeft(), topPadding,
18                 header.getPaddingRight(), header.getPaddingBottom());
19         //使header无效,将来调用onDraw()重绘View
20         header.invalidate();
21     }
22     /**
23      * 通知父布局,占用的宽和高
24      */
25     private void measureView(View view){
26         //LayoutParams are used by views to tell their parents
27         //how they want to be laid out.
28         //LayoutParams被view用来告诉它们的父布局它们应该被怎样安排
29         ViewGroup.LayoutParams p = view.getLayoutParams();
30         if(p==null){
31             //两个参数:width,height
32             p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
33                     ViewGroup.LayoutParams.WRAP_CONTENT);
34         }
35         //getChildMeasureSpec:获取子View的widthMeasureSpec、heightMeasureSpec值
36         int width = ViewGroup.getChildMeasureSpec(0, 0, p.width);
37         int height;
38         int tempHeight = p.height;
39         if(tempHeight>0){
40             height = MeasureSpec.makeMeasureSpec(tempHeight, MeasureSpec.EXACTLY);
41         }else{
42             height = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
43         }
44         view.measure(width, height);
45     }

运行效果图:

3.监听用户滑动屏幕操作

1.实现OnScrollListener接口

2.根据用户按下、移动、抬起手势来设置不同的响应事件

3.顶部提示View--header有四种状态:

1. 正常状态(NONE)

2.提示用户下拉可以刷新(PULL)

3.提示用户释放可以刷新(RELEASE)

4.提示用户正在刷新状态(REFREASH)

 1 public class RefreshListView extends ListView implements OnScrollListener{
 2         View header;//下拉刷新时出现的顶部布局View
 3     int headerHeight;//header的高度
 4     int firstVisibleItem;//当前界面第一个可见的item的位置
 5
 6     boolean isFlag;//标志,是在当前显示的listView是在listView最顶端时按下额
 7     int startY;//用户按下的Y值
 8
 9
10     int state;//当前状态
11     final int NONE = 0;//正常状态
12     final int PULL = 1;//提示下拉状态
13     final int RELEASE = 2;//提示释放状态
14     final int REFRESH = 3;//提示正在刷新状态
15
16
17         @Override
18     public void onScrollStateChanged(AbsListView view, int scrollState) {
19         // TODO Auto-generated method stub
20         this.scrollState = scrollState;
21     }
22     @Override
23     public void onScroll(AbsListView view, int firstVisibleItem,
24             int visibleItemCount, int totalItemCount) {
25         // TODO Auto-generated method stub
26         this.firstVisibleItem = firstVisibleItem;
27     }
28     @Override
29     public boolean onTouchEvent(MotionEvent ev) {
30         // TODO Auto-generated method stub
31         switch (ev.getAction()) {
32         case MotionEvent.ACTION_DOWN:
33             if(firstVisibleItem == 0){
34                 isFlag = true;//ListView最顶端按下,标志值设为真
35                 startY = (int)ev.getY();
36             }
37             break;
38         case MotionEvent.ACTION_MOVE:
39             onMove(ev);
40             break;
41         case MotionEvent.ACTION_UP:
42             if(state == RELEASE){
43                 state = REFRESH;
44                 //加载数据
45                 refreshViewByState();
46                 iRefreshlistener.onRefresh();
47             }else if(state == PULL){
48                 state = NONE;
49                 isFlag = false;
50                 refreshViewByState();
51             }
52             break;
53         }
54         return super.onTouchEvent(ev);
55     

onMove()方法----->根据用户下拉距离的不同设置不同的响应方法

 1 private void onMove(MotionEvent ev){
 2         //如果不是最顶端按下,则直接返回
 3         if(!isFlag){
 4             return;
 5         }
 6         int currentY = (int)ev.getY();//当前的Y值
 7         int space = currentY - startY;//用户向下拉的距离
 8         int topPadding = space - headerHeight;//顶部提示View距顶部的距离值
 9         switch (state) {
10         //正常状态
11         case NONE:
12             if(space>0){
13                 state = PULL;//下拉的距离大于0,则将状态改为PULL(提示下拉更新)
14                 refreshViewByState();//根据状态的不同更新View
15             }
16             break;
17         case PULL:
18             topPadding(topPadding);
19             if(space>headerHeight+30//下拉的距离大于header的高度加30且用户滚动屏幕,手指仍在屏幕上
20                     &&scrollState == SCROLL_STATE_TOUCH_SCROLL ){
21                 state = RELEASE;//将状态改为RELEASE(提示松开更新)
22                 refreshViewByState();
23             }
24             break;
25         case RELEASE:
26             topPadding(topPadding);
27             if(space<headerHeight+30){//用户下拉的距离不够
28                 state = PULL;         //将状态改为PULL
29                 refreshViewByState();
30             }else if(space<=0){  //用户下拉的距离为非正值
31                 state = NONE;    //将状态改为NONE
32                 isFlag = false;  //标志改为false
33                 refreshViewByState();
34             }
35             break;
36         }
37     }

根据不同状态,改变用户界面的显示

 1     /**
 2      * 根据当前状态state,改变界面显示
 3      * state:
 4      *      NONE:无操作
 5      *      PULL:下拉可以刷新
 6      *      RELEASE:松开可以刷新
 7      *      REFREASH:正在刷新
 8      */
 9     private void refreshViewByState(){
10         //提示
11         TextView tips = (TextView)header.findViewById(R.id.tips);
12         //箭头
13         ImageView arrow = (ImageView)header.findViewById(R.id.arrow);
14         //进度条
15         ProgressBar progress = (ProgressBar)header.findViewById(R.id.progress);
16         //箭头的动画效果1,由0度转向180度,即箭头由朝下转为朝上
17         RotateAnimation animation1 = new RotateAnimation(0, 180,
18                 RotateAnimation.RELATIVE_TO_SELF,0.5f,
19                 RotateAnimation.RELATIVE_TO_SELF,0.5f);
20         animation1.setDuration(500);
21         animation1.setFillAfter(true);
22         //箭头的动画效果2,由180度转向0度,即箭头由朝上转为朝下
23         RotateAnimation animation2 = new RotateAnimation(180, 0,
24                 RotateAnimation.RELATIVE_TO_SELF,0.5f,
25                 RotateAnimation.RELATIVE_TO_SELF,0.5f);
26         animation2.setDuration(500);
27         animation2.setFillAfter(true);
28
29         switch (state) {
30         case NONE:                     //正常状态
31             arrow.clearAnimation();    //清除箭头动画效果
32             topPadding(-headerHeight); //设置header距离顶部的距离
33             break;
34
35         case PULL:                                //下拉状态
36             arrow.setVisibility(View.VISIBLE);    //箭头设为可见
37             progress.setVisibility(View.GONE);    //进度条设为不可见
38             tips.setText("下拉可以刷新");           //提示文字设为"下拉可以刷新"
39             arrow.clearAnimation();               //清除之前的动画效果,并将其设置为动画效果2
40             arrow.setAnimation(animation2);
41             break;
42
43         case RELEASE:                            //下拉状态
44             arrow.setVisibility(View.VISIBLE);   //箭头设为可见
45             progress.setVisibility(View.GONE);   //进度条设为不可见
46             tips.setText("松开可以刷新");          //提示文字设为"松开可以刷新"
47             arrow.clearAnimation();              //清除之前的动画效果,并将其设置为动画效果2
48             arrow.setAnimation(animation1);
49             break;
50
51         case REFRESH:                             //更新状态
52             topPadding(50);                       //距离顶部的距离设置为50
53             arrow.setVisibility(View.GONE);       //箭头设为不可见
54             progress.setVisibility(View.VISIBLE); //进度条设为可见
55             tips.setText("正在刷新...");            //提示文字设为""正在刷新..."
56             arrow.clearAnimation();                //清除动画效果
57             break;
58
59         }
60     }

4.更新数据      由于在RefreshListView中不能直接更新数据,必须设置回调接口来实现更新数据这一功能。

     IRefreshListener iRefreshlistener;//刷新数据的接口
      ...
       public void setInterface(IRefreshListener listener){
        this.iRefreshlistener = listener;
    }
        /**
     * 刷新数据接口
     * @author lenovo
     *
     */
    public interface IRefreshListener{
        public void onRefresh();
    }

在MainActivity中实现IRefreshListener接口并实现onRefresh()方法

public class MainActivity extends Activity implements IRefreshListener{
   ......
   @Override
    public void onRefresh() {
        // TODO Auto-generated method stub
        //handler设置刷新延时效果
        Handler handler = new Handler();
        handler.postDelayed(new Runnable() {

            @Override
            public void run() {
                // TODO Auto-generated method stub
                //获取最新数据
                getRefreshData();
                //通知界面显示
                adapter.notifyDataSetChanged();
                //通知listView刷新数据完毕
                listView.refreshComplete();
            }
        }, 2000);
}

刷新完成的操作:

 1 public void refreshComplete(){
 2         state = NONE;   //状态设为正常状态
 3         isFlag = false; //标志设为false
 4         refreshViewByState();
 5         //设置提示更新时间间隔
 6         Time t = new Time();
 7         t.setToNow();
 8         time = t.hour*60+t.minute-updateTime;
 9         updateTime = t.hour*60+t.minute;
10         TextView lastUpdateTime = (TextView)findViewById(R.id.time);
11         lastUpdateTime.setText(time+"分钟前更新");
12     }

Demo运行效果图

时间: 2024-10-12 22:51:27

Android学习之——ListView下拉刷新的相关文章

[Android学习系列15]下拉刷新列表实现动态加载

使用google官方的SwipeRefreshLayout 参考: http://blog.csdn.net/easyer2012/article/details/22857807 http://stormzhang.github.io/android/2014/03/29/android-swiperefreshlayout/ http://www.eoeandroid.com/thread-330439-1-1.html http://www.oschina.net/translate/sw

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

这篇博客为大家介绍一个android常见的功能——ListView下拉刷新(参考自他人博客,网址忘记了,阅读他的代码自己理解注释的,希望能帮助到大家): 首先下拉未松手时候手机显示这样的界面: 下面的代码是自定的扎样的控件: package com.dhsr.smartID.view; import android.content.Context; import android.util.AttributeSet; import android.view.Gravity; import andr

Android ListView 下拉刷新 点击加载更多

最近项目中用到了ListView的下拉刷新的功能,总结了一下前辈们的代码,单独抽取出来写了一个demo作为示例. 效果图 下拉刷新: 加载更多: CustomListView.java [java] view plaincopy package com.example.uitest.view; import java.util.Date; import com.example.uitest.R; import android.content.Context; import android.uti

基于Android的计步器(Pedometer)的讲解(六)——ListView下拉刷新页面

计步器(Pedometer)整个项目的源代码,最近做了比较大的修改,可能以前下载的不能运行,感兴趣的朋友可以下载来看看(记得帮小弟在github打个星~) https://github.com/296777513/pedometer 今天实现实现的下拉刷新的功能,先上几张效果图: 如图所示,今天就是要实现的这个效果 首先,分析ListView下拉刷新实现方式 1.需要添加顶部下拉加载页面 2.需要监听onScrollListener来判断当前是否显示在listview的最顶部 3.因为顶部下拉加

Android自定义ListView下拉刷新

实现的目标是本地有数据并没有刷新.下拉数据及时刷新数据. 我在网上找了某位写的MyListView,这个东西的下拉核心部分还是没有弄明白.非常感谢这位作者. XML布局文件源代码: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layou

IOS学习之UiTableView下拉刷新与自动加载更多,百年不变的效果

IOS学习之UiTableView下拉刷新与自动加载更多,百年不变的效果(五) 五一劳动节马上来临,小伙伴有妹有很激动哟,首先祝天下所有的程序猿节日快乐!这个五一对于我来说有点不一样,我的人生从这个五一就转弯了,爱情长跑8年的我结婚了,一会支付宝账号我会公布出去,请自觉打款!谢谢合作. 灯光闪起来: 舞蹈跳起来: 歌曲唱起来: -------------------------------------------------------------------------------------

Android打造通用的下拉刷新组件

还记得上一篇 blog 的内容吗?如果不记得建议先去了解一下,Android 事件处理全面剖析 ,因为下拉刷新需要用到手势的处理,而上一篇文章中,对事件处理做了很详细的说明,了解了事件的处理机制,对理解本篇文章有很大的帮助.好了,这里就当大家都已经对事件处理有了一定的了解,开始我们的下拉刷新征程. 还是老规矩,先上效果图,再根据效果图来分析实现的原理: 一 .分析原理 我们都知道,listView 控件为我们提供了 addHeaderView.和 addFootView 的方法,我们通过此方法可

十分钟实现ListView下拉刷新上滑加载更多

说到ListView下拉刷新几乎每个APP都会用到,所以ListView下拉刷新是很重要的,就像ListView优化一样是你必会的东西. ListView实现下拉刷新如果我们开发人员自己编写相对来说比较费事的,当我们使用第三方库之后我们再来开发这个功能就会省事很多.相比与自己实现可以少编写不少代码,Android-PullToRefresh库可以轻松实现ListView的下拉刷新功能. 要使用Android—PullToRefesh库对ListView实现下拉刷新要经过以下几个步骤: 1.下载A

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

在Android中ListView下拉刷新.上拉载入更多示例一文中,Maxwin兄给出的控件比较强大,前面有详细介绍,但是有个不足就是,里面使用了一些资源文件,包括图片,String,layout,这样不利于封装打包,下面我将源码进行改进,所有布局全部用代码实现,这样直接将src和assets打包成jar就成了一个非常方便以后使用的扩展ListView控件,代码如下: XListView: /** * @file XListView.java * @package me.maxwin.view