Android UI之下拉刷新上拉刷新实现

时间:2024-03-07 15:25:23

在实际开发中我们经常要用到上拉刷新和下拉刷新,因此今天我写了一个上拉和下拉刷新的demo,有一个自定义的下拉刷新控件

只需要在布局文件中直接引用就可以使用,非常方便,非常使用,以下是源代码;

 

自定义的ListView RTPullListView

  1 package com.ryantang.pulllistview;
  2 
  3 import java.util.Date;
  4 
  5 import android.content.Context;
  6 import android.util.AttributeSet;
  7 import android.util.Log;
  8 import android.view.LayoutInflater;
  9 import android.view.MotionEvent;
 10 import android.view.View;
 11 import android.view.ViewGroup;
 12 import android.view.animation.LinearInterpolator;
 13 import android.view.animation.RotateAnimation;
 14 import android.widget.AbsListView;
 15 import android.widget.AbsListView.OnScrollListener;
 16 import android.widget.BaseAdapter;
 17 import android.widget.ImageView;
 18 import android.widget.LinearLayout;
 19 import android.widget.ListView;
 20 import android.widget.ProgressBar;
 21 import android.widget.TextView;
 22 
 23 public class RTPullListView extends ListView implements OnScrollListener {  
 24     private static final String TAG = "RTPullListView";
 25 
 26     private final static int RELEASE_To_REFRESH = 0;
 27     private final static int PULL_To_REFRESH = 1;
 28     private final static int REFRESHING = 2;
 29     private final static int DONE = 3;
 30     private final static int LOADING = 4;
 31 
 32     // 实际的padding的距离与界面上偏移距离的比例
 33     private final static int RATIO = 3;
 34     private LayoutInflater inflater;
 35     private LinearLayout headView;
 36     private TextView tipsTextview;
 37     private TextView lastUpdatedTextView;
 38     private ImageView arrowImageView;
 39     private ProgressBar progressBar;
 40 
 41     private RotateAnimation animation;
 42     private RotateAnimation reverseAnimation;
 43 
 44     // 用于保证startY的值在一个完整的touch事件中只被记录一次
 45     private boolean isRecored;
 46 
 47 //    private int headContentWidth;
 48     private int headContentHeight;
 49 
 50     private int startY;
 51     private int firstItemIndex;
 52     private int state;
 53     private boolean isBack;
 54     private OnRefreshListener refreshListener;
 55 
 56     private boolean isRefreshable;
 57     private boolean isPush;
 58 
 59     private int visibleLastIndex;
 60     private int visibleItemCount;
 61 
 62     public RTPullListView(Context context) {
 63         super(context);
 64         init(context);
 65     }
 66 
 67     public RTPullListView(Context context, AttributeSet attrs) {
 68         super(context, attrs);
 69         init(context);
 70     }
 71     
 72     private void init(Context context) {
 73         inflater = LayoutInflater.from(context);
 74         headView = (LinearLayout) inflater.inflate(R.layout.pulllist_head, null);
 75         arrowImageView = (ImageView) headView.findViewById(R.id.head_arrowImageView);
 76 //        arrowImageView.setMinimumWidth(70);
 77 //        arrowImageView.setMinimumHeight(50);
 78         progressBar = (ProgressBar) headView.findViewById(R.id.head_progressBar);
 79         tipsTextview = (TextView) headView.findViewById(R.id.head_tipsTextView);
 80         lastUpdatedTextView = (TextView) headView.findViewById(R.id.head_lastUpdatedTextView);
 81 
 82         measureView(headView);
 83         headContentHeight = headView.getMeasuredHeight();
 84 //        headContentWidth = headView.getMeasuredWidth();
 85 
 86         headView.setPadding(0, -1 * headContentHeight, 0, 0);
 87         headView.invalidate();
 88 
 89         addHeaderView(headView, null, false);
 90         setOnScrollListener(this);
 91 
 92         animation = new RotateAnimation(0, -180,
 93                 RotateAnimation.RELATIVE_TO_SELF, 0.5f,
 94                 RotateAnimation.RELATIVE_TO_SELF, 0.5f);
 95         animation.setInterpolator(new LinearInterpolator());
 96         animation.setDuration(250);
 97         animation.setFillAfter(true);
 98 
 99         reverseAnimation = new RotateAnimation(-180, 0,
100                 RotateAnimation.RELATIVE_TO_SELF, 0.5f,
101                 RotateAnimation.RELATIVE_TO_SELF, 0.5f);
102         reverseAnimation.setInterpolator(new LinearInterpolator());
103         reverseAnimation.setDuration(200);
104         reverseAnimation.setFillAfter(true);
105 
106         state = DONE;
107         isRefreshable = false;
108         isPush = true;
109     }
110     
111     public void onScroll(AbsListView arg0, int firstVisiableItem, int arg2,
112             int arg3) {
113         firstItemIndex = firstVisiableItem;
114         visibleLastIndex = firstVisiableItem + arg2 - 1; 
115         visibleItemCount = arg2;
116         if(firstItemIndex == 1 && !isPush){
117             setSelection(0);
118         }
119     }
120     
121     public void setSelectionfoot(){
122         this.setSelection(visibleLastIndex - visibleItemCount + 1);
123     }
124 
125     public void onScrollStateChanged(AbsListView arg0, int arg1) {
126         
127     }
128     @Override
129     public boolean onTouchEvent(MotionEvent event) {
130 
131         if (isRefreshable) {
132             switch (event.getAction()) {
133             case MotionEvent.ACTION_DOWN:
134                 if (firstItemIndex == 0 && !isRecored) {
135                     isRecored = true;
136                     isPush = true;
137                     startY = (int) event.getY();
138                     Log.v(TAG, "在down时候记录当前位置‘");
139                 }
140                 break;
141             case MotionEvent.ACTION_UP:
142                 if (state != REFRESHING && state != LOADING) {
143                     if (state == DONE) {
144                         // 什么都不做
145                     }
146                     if (state == PULL_To_REFRESH) {
147                         state = DONE;
148                         changeHeaderViewByState();
149 
150                         Log.v(TAG, "由下拉刷新状态,到done状态");
151                     }
152                     if (state == RELEASE_To_REFRESH) {
153                         state = REFRESHING;
154                         changeHeaderViewByState();
155                         onRefresh();
156 
157                         Log.v(TAG, "由松开刷新状态,到done状态");
158                     }
159                 }
160 
161                 isRecored = false;
162                 isBack = false;
163 
164                 break;
165 
166             case MotionEvent.ACTION_MOVE:
167                 int tempY = (int) event.getY();
168 
169                 if (!isRecored && firstItemIndex == 0) {
170                     Log.v(TAG, "在move时候记录下位置");
171                     isRecored = true;
172                     startY = tempY;
173                 }
174 
175                 if (state != REFRESHING && isRecored && state != LOADING) {
176 
177                     // 保证在设置padding的过程中,当前的位置一直是在head,否则如果当列表超出屏幕的话,当在上推的时候,列表会同时进行滚动
178 
179                     // 可以松手去刷新了
180                     if (state == RELEASE_To_REFRESH) {
181 
182                         setSelection(0);
183 
184                         // 往上推了,推到了屏幕足够掩盖head的程度,但是还没有推到全部掩盖的地步
185                         if (((tempY - startY) / RATIO < headContentHeight)
186                                 && (tempY - startY) > 0) {
187                             state = PULL_To_REFRESH;
188                             changeHeaderViewByState();
189 
190                             Log.v(TAG, "由松开刷新状态转变到下拉刷新状态");
191                         }
192                         // 一下子推到顶了
193                         else if (tempY - startY <= 0) {
194                             state = DONE;
195                             changeHeaderViewByState();
196 
197                             Log.v(TAG, "由松开刷新状态转变到done状态");
198                         }
199                         // 往下拉了,或者还没有上推到屏幕顶部掩盖head的地步
200                         else {
201                             // 不用进行特别的操作,只用更新paddingTop的值就行了
202                         }
203                     }
204                     // 还没有到达显示松开刷新的时候,DONE或者是PULL_To_REFRESH状态
205                     if (state == PULL_To_REFRESH) {
206 
207                         setSelection(0);
208 
209                         // 下拉到可以进入RELEASE_TO_REFRESH的状态
210                         if ((tempY - startY) / RATIO >= headContentHeight) {
211                             state = RELEASE_To_REFRESH;
212                             isBack = true;
213                             changeHeaderViewByState();
214                             Log.v(TAG, "由done或者下拉刷新状态转变到松开刷新");
215                         }
216                         // 上推到顶了
217                         else if (tempY - startY <= 0) {
218                             state = DONE;
219                             changeHeaderViewByState();
220                             isPush = false;
221                             Log.v(TAG, "由DOne或者下拉刷新状态转变到done状态");
222                         }
223                     }
224 
225                     // done状态下
226                     if (state == DONE) {
227                         if (tempY - startY > 0) {
228                             state = PULL_To_REFRESH;
229                             changeHeaderViewByState();
230                         }
231                     }
232 
233                     // 更新headView的size
234                     if (state == PULL_To_REFRESH) {
235                         headView.setPadding(0, -1 * headContentHeight
236                                 + (tempY - startY) / RATIO, 0, 0);
237 
238                     }
239 
240                     // 更新headView的paddingTop
241                     if (state == RELEASE_To_REFRESH) {
242                         headView.setPadding(0, (tempY - startY) / RATIO
243                                 - headContentHeight, 0, 0);
244                     }
245 
246                 }
247 
248                 break;
249             }
250         }
251 
252         return super.onTouchEvent(event);
253     }
254 
255     // 当状态改变时候,调用该方法,以更新界面
256     private void changeHeaderViewByState() {
257         switch (state) {
258         case RELEASE_To_REFRESH:
259             arrowImageView.setVisibility(View.VISIBLE);
260             progressBar.setVisibility(View.GONE);
261             tipsTextview.setVisibility(View.VISIBLE);
262             lastUpdatedTextView.setVisibility(View.VISIBLE);
263 
264             arrowImageView.clearAnimation();
265             arrowImageView.startAnimation(animation);
266 
267             tipsTextview.setText(getResources().getString(R.string.release_to_refresh));
268 
269             Log.v(TAG, "当前状态,松开刷新");
270             break;
271         case PULL_To_REFRESH:
272             progressBar.setVisibility(View.GONE);
273             tipsTextview.setVisibility(View.VISIBLE);
274             lastUpdatedTextView.setVisibility(View.VISIBLE);
275             arrowImageView.clearAnimation();
276             arrowImageView.setVisibility(View.VISIBLE);
277             // 是由RELEASE_To_REFRESH状态转变来的
278             if (isBack) {
279                 isBack = false;
280                 arrowImageView.clearAnimation();
281                 arrowImageView.startAnimation(reverseAnimation);
282 
283                 tipsTextview.setText(getResources().getString(R.string.pull_to_refresh));
284             } else {
285                 tipsTextview.setText(getResources().getString(R.string.pull_to_refresh));
286             }
287             Log.v(TAG, "当前状态,下拉刷新");
288             break;
289 
290         case REFRESHING:
291 
292             headView.setPadding(0, 0, 0, 0);
293 
294             progressBar.setVisibility(View.VISIBLE);
295             arrowImageView.clearAnimation();
296             arrowImageView.setVisibility(View.GONE);
297             tipsTextview.setText(getResources().getString(R.string.refreshing));
298             lastUpdatedTextView.setVisibility(View.VISIBLE);
299 
300             Log.v(TAG, "当前状态,正在刷新...");
301             break;
302         case DONE:
303             headView.setPadding(0, -1 * headContentHeight, 0, 0);
304 
305             progressBar.setVisibility(View.GONE);
306             arrowImageView.clearAnimation();
307             arrowImageView.setImageResource(R.drawable.pulltorefresh);
308             tipsTextview.setText(getResources().getString(R.string.pull_to_refresh));
309             lastUpdatedTextView.setVisibility(View.VISIBLE);
310 
311             Log.v(TAG, "当前状态,done");
312             break;
313         }
314     }
315 
316     public void setonRefreshListener(OnRefreshListener refreshListener) {
317         this.refreshListener = refreshListener;
318         isRefreshable = true;
319     }
320 
321     public interface OnRefreshListener {
322         public void onRefresh();
323     }
324 
325     public void onRefreshComplete() {
326         state = DONE;
327         lastUpdatedTextView.setText(getResources().getString(R.string.updating) + new Date().toLocaleString());
328         changeHeaderViewByState();
329         invalidateViews();
330         setSelection(0);
331     }
332 
333     private void onRefresh() {
334         if (refreshListener != null) {
335             refreshListener.onRefresh();
336         }
337     }
338     
339     public void clickToRefresh(){
340         state = REFRESHING;
341         changeHeaderViewByState();
342     }
343     
344     // 此方法直接照搬自网络上的一个下拉刷新的demo,此处是“估计”headView的width以及height
345     private void measureView(View child) {
346         ViewGroup.LayoutParams p = child.getLayoutParams();
347         if (p == null) {
348             p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
349                     ViewGroup.LayoutParams.WRAP_CONTENT);
350         }
351         int childWidthSpec = ViewGroup.getChildMeasureSpec(0, 0 + 0, p.width);
352         int lpHeight = p.height;
353         int childHeightSpec;
354         if (lpHeight > 0) {
355             childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight,
356                     MeasureSpec.EXACTLY);
357         } else {
358             childHeightSpec = MeasureSpec.makeMeasureSpec(0,MeasureSpec.UNSPECIFIED);
359         }
360         child.measure(childWidthSpec, childHeightSpec);
361     }
362 
363     public void setAdapter(BaseAdapter adapter) {
364         lastUpdatedTextView.setText(getResources().getString(R.string.updating) + new Date().toLocaleString());
365         super.setAdapter(adapter);
366     }
367 }  


以上就是有下拉和上拉刷新功能的自定义的listview,下拉刷新头和上拉刷新的底部样式都可以自定义

这里给出我的下拉头布局和上拉底部布局,你们可以在此基础上进行修改自定义;

pulllist_head.xml 头布局

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <!-- ListView的头部 -->
 3 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 4     android:layout_width="fill_parent"
 5     android:layout_height="wrap_content"
 6     android:paddingTop="5dp"
 7     android:paddingBottom="5dp"
 8     android:background="#FFFFFFFF" >
 9 
10     <!-- 内容 -->
11 
12     <RelativeLayout
13         android:id="@+id/head_contentLayout"
14         android:layout_width="fill_parent"
15         android:layout_height="wrap_content"
16         android:layout_marginTop="10dp"
17         android:layout_marginBottom="10dp"
18         android:paddingLeft="10dp" >
19 
20         <!-- 箭头图像、进度条 -->
21 
22         <FrameLayout
23             android:layout_width="wrap_content"
24             android:layout_height="wrap_content"
25             android:layout_alignParentLeft="true"
26             android:layout_centerVertical="true" >
27 
28             <!-- 箭头 -->
29 
30             <ImageView
31                 android:id="@+id/head_arrowImageView"
32                 android:layout_width="wrap_content"
33                 android:layout_height="wrap_content"
34                 android:layout_gravity="center"
35                 android:src="@drawable/pulltorefresh" />
36 
37             <!-- 进度条 -->
38 
39             <ProgressBar
40                 android:id="@+id/head_progressBar"
41                 style="@android:style/Widget.ProgressBar.Small"
42                 android:layout_width="wrap_content"
43                 android:layout_height="wrap_content"
44                 android:layout_gravity="center"
45                 android:visibility="gone" />
46         </FrameLayout>
47 
48         <!-- 提示、最近更新 -->
49 
50         <LinearLayout
51             android:layout_width="wrap_content"
52             android:layout_height="wrap_content"
53             android:layout_centerHorizontal="true"
54             android:gravity="center_horizontal"
55             android:orientation="vertical" >
56 
57             <!-- 提示 -->
58 
59             <TextView
60                 android:id="@+id/head_tipsTextView"
61                 android:layout_width="wrap_content"
62                 android:layout_height="wrap_content"
63                 android:textSize="16sp" />
64 
65             <!-- 最近更新 -->
66 
67             <TextView
68                 android:id="@+id/head_lastUpdatedTextView"
69                 android:layout_width="wrap_content"
70                 android:layout_height="wrap_content"
71                 android:textSize="13sp" />
72         </LinearLayout>
73     </RelativeLayout>
74 
75 </LinearLayout>

底部布局:list_footview.xml

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 3     android:id="@+id/list_footview"
 4     android:layout_width="fill_parent"
 5     android:layout_height="fill_parent"
 6     android:background="@android:color/white"
 7     android:orientation="vertical" >
 8 
 9     <LinearLayout
10         android:layout_width="wrap_content"
11         android:layout_height="wrap_content"
12         android:layout_centerInParent="true"
13         android:layout_centerVertical="true"
14         android:layout_marginBottom="25dp"
15         android:layout_marginTop="25dp"
16         android:gravity="center"
17         android:orientation="horizontal" >
18 
19         <TextView
20             android:id="@+id/text_view"
21             android:layout_width="wrap_content"
22             android:layout_height="wrap_content"
23             android:gravity="center"
24             android:text="获取更多"
25             android:textColor="@android:color/black"
26             android:textSize="16sp" />
27 
28         <ProgressBar
29             android:id="@+id/footer_progress"
30             style="?android:attr/progressBarStyleSmall"
31             android:layout_width="wrap_content"
32             android:layout_height="wrap_content"
33             android:layout_marginLeft="3dp"
34             android:visibility="gone" />
35     </LinearLayout>
36 
37 </RelativeLayout>

 

ok这样一个自定义的下拉上拉刷新的listview就定义好了,可以在activity的布局文件中直接引用,并且像操纵listview那样使用

非常方便,这里也给出我使用的源码;

首先是主界面布局:main.xml

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 3     android:layout_width="fill_parent"
 4     android:layout_height="fill_parent"
 5     android:orientation="vertical" >
 6 
 7     <com.ryantang.pulllistview.RTPullListView
 8         android:id="@+id/pullListView"
 9         android:layout_width="fill_parent"
10         android:layout_height="wrap_content" />
11 
12 </LinearLayout>

 

主界面activity中使用源码:

  1 package com.ryantang.pulllistview;
  2 
  3 import java.util.ArrayList;
  4 import java.util.List;
  5 
  6 import android.app.Activity;
  7 import android.os.Bundle;
  8 import android.os.Handler;
  9 import android.os.Message;
 10 import android.view.LayoutInflater;
 11 import android.view.View;
 12 import android.view.View.OnClickListener;
 13 import android.widget.ArrayAdapter;
 14 import android.widget.ProgressBar;
 15 import android.widget.RelativeLayout;
 16 
 17 import com.ryantang.pulllistview.RTPullListView.OnRefreshListener;
 18 
 19 /**
 20  * PullListView
 21  * @author Ryan
 22  *
 23  */
 24 public class RTPullListViewActivity extends Activity {
 25     private static final int INTERNET_FAILURE = -1;
 26     private static final int LOAD_SUCCESS = 1;
 27     private static final int LOAD_MORE_SUCCESS = 3;
 28     private static final int NO_MORE_INFO = 4;
 29     private static final int LOAD_NEW_INFO = 5;
 30     private RTPullListView pullListView;
 31     private ProgressBar moreProgressBar;
 32     private List<String> dataList;
 33     private ArrayAdapter<String> adapter;
 34     
 35     @Override
 36     public void onCreate(Bundle savedInstanceState) {
 37         super.onCreate(savedInstanceState);
 38         setContentView(R.layout.main);
 39         pullListView = (RTPullListView) this.findViewById(R.id.pullListView);
 40         
 41         dataList = new ArrayList<String>();
 42         for (int i = 0; i < 5; i++) {
 43             dataList.add("Item data "+i);
 44         }
 45         adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, dataList);
 46         pullListView.setAdapter(adapter);
 47         
 48         //添加listview底部获取更多按钮(可自定义)
 49         LayoutInflater inflater = LayoutInflater.from(this);
 50         View view = inflater.inflate(R.layout.list_footview, null);
 51         RelativeLayout footerView =(RelativeLayout) view.findViewById(R.id.list_footview);
 52         moreProgressBar = (ProgressBar) view.findViewById(R.id.footer_progress);
 53         pullListView.addFooterView(footerView);
 54         
 55         //下拉刷新监听器
 56         pullListView.setonRefreshListener(new OnRefreshListener() {
 57             
 58             @Override
 59             public void onRefresh() {
 60                 new Thread(new Runnable() {
 61                     
 62                     @Override
 63                     public void run() {
 64                         try {
 65                             Thread.sleep(2000);
 66                             dataList.clear();
 67                             for (int i = 0; i < 5; i++) {
 68                                 dataList.add("Item data "+i);
 69                             }
 70                             myHandler.sendEmptyMessage(LOAD_NEW_INFO);
 71                         } catch (InterruptedException e) {
 72                             e.printStackTrace();
 73                         }
 74                     }
 75                 }).start();
 76             }
 77         });
 78         
 79         //获取跟多监听器
 80         footerView.setOnClickListener(new OnClickListener() {//
 81             
 82             @Override
 83             public void onClick(View v) {
 84 
 85                 moreProgressBar.setVisibility(View.VISIBLE);
 86                 
 87                 new Thread(new Runnable() {
 88                     
 89                     @Override
 90                     public void run() {
 91                         try {
 92                             Thread.sleep(2000);
 93                             for (int i = 0; i < 5; i++) {
 94                                 dataList.add("New item data "+i);
 95                             }
 96                             myHandler.sendEmptyMessage(LOAD_MORE_SUCCESS);
 97                         } catch (InterruptedException e) {
 98                             e.printStackTrace();
 99                         }
100                     }
101                 }).start();
102             }
103         });
104     }
105     
106     //结果处理
107     private Handler myHandler = new Handler(){
108 
109         @Override
110         public void handleMessage(Message msg) {
111             super.handleMessage(msg);
112             switch (msg.what) {
113             case LOAD_MORE_SUCCESS:
114                 moreProgressBar.setVisibility(View.GONE);
115                 adapter.notifyDataSetChanged();
116                 pullListView.setSelectionfoot();
117                 break;
118 
119             case LOAD_NEW_INFO:
120                 adapter.notifyDataSetChanged();
121                 pullListView.onRefreshComplete();
122                 break;
123             default:
124                 break;
125             }
126         }
127         
128     };
129 }

 效果图如下: