该准备的东西都已经准备好了。在这篇文章里,我们就开始实现下拉刷新功能吧。 

一、大体的逻辑分析 

      我们来简单分析一下需要做的逻辑吧。首先分析头布局有几种状态。不下拉时,为正常状态,此时头布局隐藏。下拉到一定高度,提示信息变为“下拉刷新”,箭头朝下,此为下拉状态。再往下拉,提示信息变为“松开刷新”,箭头朝上,此为提示刷新状态。而此时松开手指,则执行刷新操作,头布局变为进度条显示,箭头消失,此为正在刷新状态。相反的,其他状态下松开手指,都不执行刷新操作,应该将头布局恢复到正常状态。因为可确定头布局的状态有四种。

      我们根据这四种状态,确定我们要做的事情。要监听ListView的滚动,故要实现OnScrollListener接口。还要监听手指触摸事件,根据手指的下拉移动来改变头布局的显示效果,根据手指的抬起来判断是否进行刷新操作,因为要实现onTouchEvent方法。也就是说,头布局状态的改变应该随着手指的移动而改变,因此在onTouchEvent里面我们要实现上面分析的四种状态的改变。当然,状态改变就意味着头布局显示效果的改变,这里可以嵌套在onTouchEvent方法里面。但考虑到避免方法臃肿,以及其他地方可能也需要改变头布局界面,比如数据加载完成后等情况,因此专门将头布局界面的改变抽取出来,凝聚为一个方法。

      然后就是数据刷新,刷新操作要在MyListView里执行,但是数据要在MainActivity中获取。老规矩,用接口回调即可。

      好了,基本上大体的逻辑就这么多了。下面我们将上面的分析转化为代码。 

二、代码编写 

      废话我就不多说了,上面的分析很清楚了。继续完善MyListView即可。代码如下:

1 package com.fuly.load;
  2 
  3 import android.content.Context;
  4 import android.util.AttributeSet;
  5 import android.view.LayoutInflater;
  6 import android.view.MotionEvent;
  7 import android.view.View;
  8 import android.view.ViewGroup;
  9 import android.widget.AbsListView;
 10 import android.widget.AbsListView.OnScrollListener;
 11 import android.widget.ImageView;
 12 import android.widget.ListView;
 13 import android.widget.ProgressBar;
 14 import android.widget.TextView;
 15 
 16 public class MyListView extends ListView implements OnScrollListener{
 17     
 18     private View header;//头布局
 19     
 20     private int headerHeight;//头布局自身的高度
 21     
 22     private int scrollState;//当前滚动状态
 23     private int firstVisibleItem;//当前可见的第一个item
 24     private int startY;//刚开始触摸屏幕时的Y值
 25     
 26     private int curState = 0;//当前header状态,默认为0
 27     private final int NORMAL = 0;//正常状态
 28     private final int PULL = 1;//状态下拉
 29     private final int RELEASE = 2;//提示刷新状态
 30     private final int RELEASING = 3;//状态正在刷新
 31     
 32     private boolean canPull = false;//是否可以执行下拉操作
 33     
 34     private refresfListener mListener;//回调接口
 35     
 36 
 37     //三个构造方法都要重写
 38     public MyListView(Context context) {
 39         super(context);
 40         initView( context);
 41         
 42     }
 43     public MyListView(Context context, AttributeSet attrs) {
 44         super(context, attrs);
 45         initView( context);
 46         
 47     }
 48     public MyListView(Context context, AttributeSet attrs, int defStyle) {
 49         super(context, attrs, defStyle);
 50         initView( context);
 51     
 52     }
 53     
 54     //定义回调接口
 55     public interface refresfListener{
 56         void refresh();
 57     }
 58     public void setOnRefreshListener(refresfListener listener){
 59         this.mListener = listener;
 60     }
 61     
 62     
 63     public void initView(Context context){
 64         
 65         header = LayoutInflater.from(context).inflate(R.layout.header, null);
 66         notifyView(header);
 67         headerHeight = header.getMeasuredHeight();//获取header的高度
 68 
 69 //        headerHeight = header.getHeight();
 70         paddingTop(-headerHeight);
 71         //将头布局加进去
 72         this.addHeaderView(header);
 73         
 74         this.setOnScrollListener(this);
 75     }
 76 
 77     
 78     
 79     
 80     /**
 81      * 该方法为通知父布局,子布局view的宽度和高度
 82      * @param view:子布局
 83      */
 84     private void notifyView(View view){
 85         
 86         ViewGroup.LayoutParams p = view.getLayoutParams();
 87         
 88         if(p == null){
 89             p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
 90             
 91         }
 92         
 93         //spec表示当前子view左右边距,padding表示子view的左右内边距
 94         //childDimension:子view的宽度
 95         int width = ViewGroup.getChildMeasureSpec(0, 0, p.width);
 96         
 97         int height;
 98         int tempHeight = p.height;
 99         if(tempHeight>0){
100             //子布局高度不为空,需要填充这个布局
101             height = MeasureSpec.makeMeasureSpec(tempHeight, MeasureSpec.EXACTLY);
102         }else{
103             //高度为0,则不需要填充
104             height = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
105         }
106         
107         //然后告诉父布局,子布局的高度和宽度
108         view.measure(width, height);    
109     }
110     
111     
112     //该方法设定header的paddingTop
113     private void paddingTop(int pt){
114         header.setPadding(header.getPaddingLeft(), pt, header.getPaddingRight(), header.getPaddingBottom());
115         header.invalidate();
116     }
117     
118     
119     
120 
121     /***
122      * 监听当前滚动状态
123      * scrollState:当前滚动状态
124      */
125     public void onScrollStateChanged(AbsListView view, int scrollState) {
126         //记录当前的滚动状态
127         this.scrollState = scrollState;
128         
129     }
130     
131     /***
132      * 监听当前滚动的item
133      * firstVisibleItem:当前可见的第一个item
134      * visibleItemCount:当前共有多少个item可见
135      * totalItemCount:总共有多少个item
136      * 
137      */
138     public void onScroll(AbsListView view, int firstVisibleItem,
139             int visibleItemCount, int totalItemCount) {
140         
141         this.firstVisibleItem = firstVisibleItem;
142         
143         
144     }
145     
146     
147     //触屏事件
148     public boolean onTouchEvent(MotionEvent ev) {
149         
150         switch(ev.getAction()){
151              //手指落到屏幕上时
152         case MotionEvent.ACTION_DOWN:
153             //如果当前可见的第一个item为第0号,说明ListView位于顶端,可以执行下拉刷新
154             if(firstVisibleItem == 0){
155                 canPull = true;
156                 startY = (int) ev.getY();
157             }
158             
159             break;
160             //手指在屏幕上拖动时
161         case MotionEvent.ACTION_MOVE:
162             if(canPull){
163                 touchMove(ev);
164             }
165             break;
166             //手指离开屏幕时
167         case MotionEvent.ACTION_UP:
168             canPull = false;
169             if(curState == RELEASE){
170                 curState = RELEASING;
171                 refreshHeaderByState();
172                 //这里添加刷新数据的逻辑
173                 mListener.refresh();
174             }else{
175                 curState = NORMAL;
176                 refreshHeaderByState();
177                 paddingTop(-headerHeight);
178             }
179             
180             break;
181         }
182         
183         return super.onTouchEvent(ev);
184     }
185     
186     /**
187      * 该方法根据触摸屏幕滑动来改变STATE,即改变当前状态
188      * @param ev
189      */
190     private void touchMove(MotionEvent ev) {
191         
192         int tempY = (int) ev.getY();
193         int space = tempY -startY;//移动的距离
194         int topdding = space-headerHeight;
195         paddingTop(topdding);//即时设定头布局的隐藏高度
196         if(space>headerHeight&&space<headerHeight+50&&scrollState == SCROLL_STATE_TOUCH_SCROLL){
197              curState = PULL;//设定为下拉状态
198              refreshHeaderByState();
199         }
200         if(space>headerHeight+50){
201             curState = RELEASE;//设定为提示刷新状态
202              refreshHeaderByState();
203         }
204         
205         if(space<headerHeight){
206             curState = NORMAL;//设定为正常状态
207              refreshHeaderByState();
208         }
209         
210         
211     }
212     
213     
214     /**
215      * 根据当前状态更改header的显示界面
216      * 
217      */
218     private void refreshHeaderByState( ){
219         ProgressBar pb = (ProgressBar) header.findViewById(R.id.progress_bar);
220         ImageView img = (ImageView) header.findViewById(R.id.img_arrow);
221         TextView tv = (TextView) header.findViewById(R.id.textinfo);
222         
223         switch(curState){
224         
225             case NORMAL:
226                 pb.setVisibility(View.GONE);
227                 img.setVisibility(View.VISIBLE);
228                 img.setImageResource(R.drawable.down_arrow);
229                 tv.setText("下拉刷新");
230             break;
231             case PULL:
232                 pb.setVisibility(View.GONE);
233                 img.setVisibility(View.VISIBLE);
234                 img.setImageResource(R.drawable.down_arrow);
235                 tv.setText("下拉刷新");
236                 break;
237             case RELEASE:
238                 pb.setVisibility(View.GONE);
239                 img.setVisibility(View.VISIBLE);
240                 img.setImageResource(R.drawable.up_arrow);
241                 tv.setText("松开刷新");
242                 break;
243             case RELEASING:
244                 pb.setVisibility(View.VISIBLE);
245                 img.setVisibility(View.GONE);
246                 tv.setText("正在刷新");
247                 break;
248             
249             
250         }
251         
252     }
253     
254     //数据刷新完成后的操作
255     public void refreshFinish(){
256         
257         curState = NORMAL;
258         paddingTop(-headerHeight);
259         refreshHeaderByState();
260     }
261     
262     
263     
264 }

       接下来就是MainActivity中的代码了。如下:

1 package com.fuly.load;
 2 
 3 import java.util.ArrayList;
 4 import java.util.List;
 5 
 6 import com.fuly.load.MyListView.refresfListener;
 7 
 8 import android.os.Bundle;
 9 import android.os.Handler;
10 import android.app.Activity;
11 
12 public class MainActivity extends Activity implements refresfListener{
13     
14     private MyListView lv;
15     private List<MyData>  mDatas = new ArrayList<MyData>();
16     private MyAdapter mAdapter;
17 
18 
19     protected void onCreate(Bundle savedInstanceState) {
20         super.onCreate(savedInstanceState);
21         setContentView(R.layout.activity_main);
22         
23         initData();//该方法初始化数据
24         lv = (MyListView) findViewById(R.id.list_view);
25         lv.setOnRefreshListener(this);//设定回调接口
26         mAdapter = new MyAdapter(this, mDatas);
27         lv.setAdapter(mAdapter);
28         
29         
30     }
31 
32 
33     /**
34      * 该方法初始化数据,即提供初始的素材
35      */
36     private void initData() {
37         for(int i = 0;i<12;i++){
38             MyData md = new MyData("你好,我是提前设定的");
39             mDatas.add(md);
40         }
41         
42     }
43       /**
44      * 提供刷新数据
45      */
46     private void getRefreshData() {
47         for(int i = 0;i<3;i++){
48             MyData md = new MyData("你好,我是刷新进来的");
49             mDatas.add(i, md);
50         }
51         
52     }
53 
54 
55     //重写回调方法
56     public void refresh() {
57         
58         //在这里之所以使用Handler,是想让操作延迟,这样子效果看起来更
59         //清晰,实际项目中,是 不需要的
60         Handler mHandler = new Handler();
61         mHandler.postDelayed(new Runnable(){
62 
63             @Override
64             public void run() {
65                 //获得刷新数据
66                 getRefreshData();
67                 //刷新ListView
68                 mAdapter.notifyDataSetChanged();
69                 //lv.setSelection(mDatas.size()-1);
70                 
71                 //刷新后
72                 lv.refreshFinish();
73                 
74             }
75             
76         }, 5000    );
77         
78         
79         
80     }
81 }

      好了,快快运行下程序,体验下拉刷新的效果吧。至此,ListView实现下拉刷新,我们讲解完毕了。