在实际开发中我们经常要用到上拉刷新和下拉刷新,因此今天我写了一个上拉和下拉刷新的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 }
效果图如下: