说到下拉刷新控件,网上版本有很多,很多软件也都有下拉刷新功能。有一个叫XListView的,我看别人用过,没看过是咋实现的,看这名字估计是继承自ListView修改的,不过效果看起来挺丑的,也没什么扩展性,太单调了。看了QQ2014的列表下拉刷新,发现挺好看的,我喜欢,贴一下图看一下qq的下拉刷新效果:
不错吧?嗯,是的。一看就知道实现方式不一样。咱们今天就来实现一个下拉刷新控件。由于有时候不仅仅是ListView需要下拉刷新,ExpandableListView和GridView也有这个需求,由于ListView,GridView都是AbsListView的子类,ExpandableListView是ListView的子类所以也是AbsListView的子类。所以我的思路是自定义一个对所有AbsListView的子类通用的下拉管理布局,叫PullToRefreshLayout,如果需要GridView,只需要在布局文件里将ListView换成GridView就行了,ExpandableListView也一样,不需要再继承什么GridView啊ListView啊乱七八糟的。
看上图,主要工作就是定义黑色大布局,红色部分是不下拉的时候的可见部分,可以是任意的AbsListView的子类(GridView,ListView,ExpandableListView等等)。其实我已经写好了,先看一下效果图:
正常拉法:
强迫症拉法:
上面是ListView的,下面是GridView的
再来看一下ExpandableListView的下拉刷新效果:
可以看到,点击事件和长按事件都能正常触发而不会误触发,在使用ExpandableListView的时候需要注意禁止展开时自动滚动,否则会出现bug。后面会提供demo源码下载,可以根据自己的需求去修改。
下面讲解PullToRefreshLayout的实现,在贴完整的源码之前先理解整个类的大概思路:
[java] view plain copy
1. public class PullToRefreshLayout extends RelativeLayout implements OnTouchListener
2. {
3.
4. // 下拉的距离
5. public float moveDeltaY = 0;
6. // 是否可以下拉
7. private boolean canPull = true;
8.
9.
10. private void hideHead()
11. {
12. // 在这里开始异步隐藏下拉头,在松手的时候或这刷新完毕的时候隐藏
13. }
14.
15.
16. public void refreshFinish(int refreshResult)
17. {
18. // 完成刷新操作,显示刷新结果
19. }
20.
21. private void changeState(int to)
22. {
23. // 改变当前所处的状态,有四个状态:下拉刷新、释放刷新、正在刷新、刷新完成
24. }
25.
26. /*
27. * (非 Javadoc)由父控件决定是否分发事件,防止事件冲突
28. *
29. * @see android.view.ViewGroup#dispatchTouchEvent(android.view.MotionEvent)
30. */
31. @Override
32. public boolean dispatchTouchEvent(MotionEvent ev)
33. {
34. switch (ev.getActionMasked())
35. {
36. case MotionEvent.ACTION_DOWN:
37. /*手指按下的时候,无法判断是否将要下拉,所以这时候break让父类把down事件分发给子View
38. 记录按下的坐标*/
39. break;
40. case MotionEvent.ACTION_MOVE:
41. /*如果往上滑动且moveDetaY==0则说明不在下拉,break继续将move事件分发给子View
42. 如果往下拉,则计算下拉的距离moveDeltaY,根据moveDeltaY重新Layout子控件。但是
43. 由于down事件传到了子View,如果不清除子View的事件,会导致子View误触发长按事件和点击事件。所以在这里清除子View的事件回调。
44. 下拉超过一定的距离时,改变当前状态*/
45. break;
46. case MotionEvent.ACTION_UP:
47. //根据当前状态执行刷新操作或者hideHead
48. default:
49. break;
50. }
51. // 事件分发交给父类
52. return super.dispatchTouchEvent(ev);
53. }
54.
55.
56.
57. /*
58. * (非 Javadoc)绘制阴影效果,颜色值可以修改
59. *
60. * @see android.view.ViewGroup#dispatchDraw(android.graphics.Canvas)
61. */
62. @Override
63. protected void dispatchDraw(Canvas canvas)
64. {
65. //在这里用一个渐变绘制分界线阴影
66. }
67.
68.
69.
70. @Override
71. protected void onLayout(boolean changed, int l, int t, int r, int b)
72. {
73. //这个方法就是重新Layout子View了,根据moveDeltaY来定位子View的位置
74. }
75.
76.
77. @Override
78. public boolean onTouch(View v, MotionEvent event)
79. {
80. //这个是OnTouchListener的方法,只判断AbsListView的状态来决定是否canPull,除此之外不做其他处理
81. }
82. }
可以看到,这里复写了ViewGroup的dispatchTouchEvent,这样就可以掌控事件的分发,如果不了解这个方法可以看一下这篇Android事件分发、View事件Listener全解析。之所以要控制事件分发是因为我们不可能知道手指down在AbsListView上之后将往上滑还是往下拉,所以down事件会分发给AbsListView的,但是在move的时候就需要看情况了,因为我们不想在下拉的同时AbsListView也在滑动,所以在下拉的时候不分发move事件,但这样问题又来了,前面AbsListView已经接收了down事件,如果这时候不分发move事件给它,它会触发长按事件或者点击事件,所以在这里还需要清除AbsListView消息列表中的callback。
onLayout用于重新布置下拉头和AbsListView的位置的,这个不难理解。
理解了大概思路之后,看一下PullToRefreshLayout完整的源码吧~
[java] view plain copy
1. package com.jingchen.pulltorefresh;
2.
3. import java.lang.reflect.Field;
4. import java.util.Timer;
5. import java.util.TimerTask;
6.
7. import android.content.Context;
8. import android.graphics.Canvas;
9. import android.graphics.LinearGradient;
10. import android.graphics.Paint;
11. import android.graphics.Paint.Style;
12. import android.graphics.RectF;
13. import android.graphics.Shader.TileMode;
14. import android.os.Handler;
15. import android.os.Message;
16. import android.util.AttributeSet;
17. import android.util.Log;
18. import android.view.MotionEvent;
19. import android.view.View;
20. import android.view.View.OnTouchListener;
21. import android.view.ViewGroup;
22. import android.view.animation.AnimationUtils;
23. import android.view.animation.LinearInterpolator;
24. import android.view.animation.RotateAnimation;
25. import android.widget.AbsListView;
26. import android.widget.RelativeLayout;
27. import android.widget.TextView;
28.
29. /**
30. * 整个下拉刷新就这一个布局,用来管理两个子控件,其中一个是下拉头,另一个是包含内容的contentView(可以是AbsListView的任何子类)
31. *
32. * @author 陈靖
33. */
34. public class PullToRefreshLayout extends RelativeLayout implements OnTouchListener
35. {
36. public static final String TAG = "PullToRefreshLayout";
37. // 下拉刷新
38. public static final int PULL_TO_REFRESH = 0;
39. // 释放刷新
40. public static final int RELEASE_TO_REFRESH = 1;
41. // 正在刷新
42. public static final int REFRESHING = 2;
43. // 刷新完毕
44. public static final int DONE = 3;
45. // 当前状态
46. private int state = PULL_TO_REFRESH;
47. // 刷新回调接口
48. private OnRefreshListener mListener;
49. // 刷新成功
50. public static final int REFRESH_SUCCEED = 0;
51. // 刷新失败
52. public static final int REFRESH_FAIL = 1;
53. // 下拉头
54. private View headView;
55. // 内容
56. private View contentView;
57. // 按下Y坐标,上一个事件点Y坐标
58. private float downY, lastY;
59. // 下拉的距离
60. public float moveDeltaY = 0;
61. // 释放刷新的距离
62. private float refreshDist = 200;
63. private Timer timer;
64. private MyTimerTask mTask;
65. // 回滚速度
66. public float MOVE_SPEED = 8;
67. // 第一次执行布局
68. private boolean isLayout = false;
69. // 是否可以下拉
70. private boolean canPull = true;
71. // 在刷新过程中滑动操作
72. private boolean isTouchInRefreshing = false;
73. // 手指滑动距离与下拉头的滑动距离比,中间会随正切函数变化
74. private float radio = 2;
75. // 下拉箭头的转180°动画
76. private RotateAnimation rotateAnimation;
77. // 均匀旋转动画
78. private RotateAnimation refreshingAnimation;
79. // 下拉的箭头
80. private View pullView;
81. // 正在刷新的图标
82. private View refreshingView;
83. // 刷新结果图标
84. private View stateImageView;
85. // 刷新结果:成功或失败
86. private TextView stateTextView;
87. /**
88. * 执行自动回滚的handler
89. */
90. new Handler()
91. {
92.
93. @Override
94. public void handleMessage(Message msg)
95. {
96. // 回弹速度随下拉距离moveDeltaY增大而增大
97. float) (8 + 5 * Math.tan(Math.PI / 2 / getMeasuredHeight() * moveDeltaY));
98. if (state == REFRESHING && moveDeltaY <= refreshDist && !isTouchInRefreshing)
99. {
100. // 正在刷新,且没有往上推的话则悬停,显示"正在刷新..."
101. moveDeltaY = refreshDist;
102. mTask.cancel();
103. }
104. if (canPull)
105. moveDeltaY -= MOVE_SPEED;
106. if (moveDeltaY <= 0)
107. {
108. // 已完成回弹
109. 0;
110. pullView.clearAnimation();
111. // 隐藏下拉头时有可能还在刷新,只有当前状态不是正在刷新时才改变状态
112. if (state != REFRESHING)
113. changeState(PULL_TO_REFRESH);
114. mTask.cancel();
115. }
116. // 刷新布局,会自动调用onLayout
117. requestLayout();
118. }
119.
120. };
121.
122. public void setOnRefreshListener(OnRefreshListener listener)
123. {
124. mListener = listener;
125. }
126.
127. public PullToRefreshLayout(Context context)
128. {
129. super(context);
130. initView(context);
131. }
132.
133. public PullToRefreshLayout(Context context, AttributeSet attrs)
134. {
135. super(context, attrs);
136. initView(context);
137. }
138.
139. public PullToRefreshLayout(Context context, AttributeSet attrs, int defStyle)
140. {
141. super(context, attrs, defStyle);
142. initView(context);
143. }
144.
145. private void initView(Context context)
146. {
147. new Timer();
148. new MyTimerTask(updateHandler);
149. rotateAnimation = (RotateAnimation) AnimationUtils.loadAnimation(context, R.anim.reverse_anim);
150. refreshingAnimation = (RotateAnimation) AnimationUtils.loadAnimation(context, R.anim.rotating);
151. // 添加匀速转动动画
152. new LinearInterpolator();
153. rotateAnimation.setInterpolator(lir);
154. refreshingAnimation.setInterpolator(lir);
155. }
156.
157. private void hideHead()
158. {
159. if (mTask != null)
160. {
161. mTask.cancel();
162. null;
163. }
164. new MyTimerTask(updateHandler);
165. 0, 5);
166. }
167.
168. /**
169. * 完成刷新操作,显示刷新结果
170. */
171. public void refreshFinish(int refreshResult)
172. {
173. refreshingView.clearAnimation();
174. refreshingView.setVisibility(View.GONE);
175. switch (refreshResult)
176. {
177. case REFRESH_SUCCEED:
178. // 刷新成功
179. stateImageView.setVisibility(View.VISIBLE);
180. stateTextView.setText(R.string.refresh_succeed);
181. stateImageView.setBackgroundResource(R.drawable.refresh_succeed);
182. break;
183. case REFRESH_FAIL:
184. // 刷新失败
185. stateImageView.setVisibility(View.VISIBLE);
186. stateTextView.setText(R.string.refresh_fail);
187. stateImageView.setBackgroundResource(R.drawable.refresh_failed);
188. break;
189. default:
190. break;
191. }
192. // 刷新结果停留1秒
193. new Handler()
194. {
195. @Override
196. public void handleMessage(Message msg)
197. {
198. state = PULL_TO_REFRESH;
199. hideHead();
200. }
201. 0, 1000);
202. }
203.
204. private void changeState(int to)
205. {
206. state = to;
207. switch (state)
208. {
209. case PULL_TO_REFRESH:
210. // 下拉刷新
211. stateImageView.setVisibility(View.GONE);
212. stateTextView.setText(R.string.pull_to_refresh);
213. pullView.clearAnimation();
214. pullView.setVisibility(View.VISIBLE);
215. break;
216. case RELEASE_TO_REFRESH:
217. // 释放刷新
218. stateTextView.setText(R.string.release_to_refresh);
219. pullView.startAnimation(rotateAnimation);
220. break;
221. case REFRESHING:
222. // 正在刷新
223. pullView.clearAnimation();
224. refreshingView.setVisibility(View.VISIBLE);
225. pullView.setVisibility(View.INVISIBLE);
226. refreshingView.startAnimation(refreshingAnimation);
227. stateTextView.setText(R.string.refreshing);
228. break;
229. default:
230. break;
231. }
232. }
233.
234. /*
235. * (非 Javadoc)由父控件决定是否分发事件,防止事件冲突
236. *
237. * @see android.view.ViewGroup#dispatchTouchEvent(android.view.MotionEvent)
238. */
239. @Override
240. public boolean dispatchTouchEvent(MotionEvent ev)
241. {
242. switch (ev.getActionMasked())
243. {
244. case MotionEvent.ACTION_DOWN:
245. downY = ev.getY();
246. lastY = downY;
247. if (mTask != null)
248. {
249. mTask.cancel();
250. }
251. /*
252. * 触碰的地方位于下拉头布局,由于我们没有对下拉头做事件响应,这时候它会给咱返回一个false导致接下来的事件不再分发进来。
253. * 所以我们不能交给父类分发,直接返回true
254. */
255. if (ev.getY() < moveDeltaY)
256. return true;
257. break;
258. case MotionEvent.ACTION_MOVE:
259. // canPull这个值在底下onTouch中会根据ListView是否滑到顶部来改变,意思是是否可下拉
260. if (canPull)
261. {
262. // 对实际滑动距离做缩小,造成用力拉的感觉
263. moveDeltaY = moveDeltaY + (ev.getY() - lastY) / radio;
264. if (moveDeltaY < 0)
265. 0;
266. if (moveDeltaY > getMeasuredHeight())
267. moveDeltaY = getMeasuredHeight();
268. if (state == REFRESHING)
269. {
270. // 正在刷新的时候触摸移动
271. true;
272. }
273. }
274. lastY = ev.getY();
275. // 根据下拉距离改变比例
276. float) (2 + 2 * Math.tan(Math.PI / 2 / getMeasuredHeight() * moveDeltaY));
277. requestLayout();
278. if (moveDeltaY <= refreshDist && state == RELEASE_TO_REFRESH)
279. {
280. // 如果下拉距离没达到刷新的距离且当前状态是释放刷新,改变状态为下拉刷新
281. changeState(PULL_TO_REFRESH);
282. }
283. if (moveDeltaY >= refreshDist && state == PULL_TO_REFRESH)
284. {
285. changeState(RELEASE_TO_REFRESH);
286. }
287. if (moveDeltaY > 8)
288. {
289. // 防止下拉过程中误触发长按事件和点击事件
290. clearContentViewEvents();
291. }
292. if (moveDeltaY > 0)
293. {
294. // 正在下拉,不让子控件捕获事件
295. return true;
296. }
297. break;
298. case MotionEvent.ACTION_UP:
299. if (moveDeltaY > refreshDist)
300. // 正在刷新时往下拉释放后下拉头不隐藏
301. false;
302. if (state == RELEASE_TO_REFRESH)
303. {
304. changeState(REFRESHING);
305. // 刷新操作
306. if (mListener != null)
307. mListener.onRefresh();
308. else
309. {
310.
311. }
312. hideHead();
313. default:
314. break;
315. }
316. // 事件分发交给父类
317. return super.dispatchTouchEvent(ev);
318. }
319.
320. /**
321. * 通过反射修改字段去掉长按事件和点击事件
322. */
323. private void clearContentViewEvents()
324. {
325. try
326. {
327. class.getDeclaredFields();
328. for (int i = 0; i < fields.length; i++)
329. if (fields[i].getName().equals("mPendingCheckForLongPress"))
330. {
331. // mPendingCheckForLongPress是AbsListView中的字段,通过反射获取并从消息列表删除,去掉长按事件
332. true);
333. contentView.getHandler().removeCallbacks((Runnable) fields[i].get(contentView));
334. else if (fields[i].getName().equals("mTouchMode"))
335. {
336. // TOUCH_MODE_REST = -1, 这个可以去除点击事件
337. true);
338. 1);
339. }
340. // 去掉焦点
341. new int[]
342. 0 });
343. catch (Exception e)
344. {
345. "error : " + e.toString());
346. }
347. }
348.
349. /*
350. * (非 Javadoc)绘制阴影效果,颜色值可以修改
351. *
352. * @see android.view.ViewGroup#dispatchDraw(android.graphics.Canvas)
353. */
354. @Override
355. protected void dispatchDraw(Canvas canvas)
356. {
357. super.dispatchDraw(canvas);
358. if (moveDeltaY == 0)
359. return;
360. new RectF(0, 0, getMeasuredWidth(), moveDeltaY);
361. new Paint();
362. true);
363. // 阴影的高度为26
364. new LinearGradient(0, moveDeltaY, 0, moveDeltaY - 26, 0x66000000, 0x00000000, TileMode.CLAMP);
365. paint.setShader(linearGradient);
366. paint.setStyle(Style.FILL);
367. // 在moveDeltaY处往上变淡
368. canvas.drawRect(rectF, paint);
369. }
370.
371. private void initView()
372. {
373. pullView = headView.findViewById(R.id.pull_icon);
374. stateTextView = (TextView) headView.findViewById(R.id.state_tv);
375. refreshingView = headView.findViewById(R.id.refreshing_icon);
376. stateImageView = headView.findViewById(R.id.state_iv);
377. }
378.
379. @Override
380. protected void onLayout(boolean changed, int l, int t, int r, int b)
381. {
382. if (!isLayout)
383. {
384. // 这里是第一次进来的时候做一些初始化
385. 0);
386. 1);
387. // 给AbsListView设置OnTouchListener
388. this);
389. true;
390. initView();
391. 0).getMeasuredHeight();
392. }
393. if (canPull)
394. {
395. // 改变子控件的布局
396. 0, (int) moveDeltaY - headView.getMeasuredHeight(), headView.getMeasuredWidth(), (int) moveDeltaY);
397. 0, (int) moveDeltaY, contentView.getMeasuredWidth(), (int) moveDeltaY + contentView.getMeasuredHeight());
398. else super.onLayout(changed, l, t, r, b);
399. }
400.
401. class MyTimerTask extends TimerTask
402. {
403. Handler handler;
404.
405. public MyTimerTask(Handler handler)
406. {
407. this.handler = handler;
408. }
409.
410. @Override
411. public void run()
412. {
413. handler.sendMessage(handler.obtainMessage());
414. }
415.
416. }
417.
418. @Override
419. public boolean onTouch(View v, MotionEvent event)
420. {
421. // 第一个item可见且滑动到顶部
422. null;
423. try
424. {
425. alv = (AbsListView) v;
426. catch (Exception e)
427. {
428. Log.d(TAG, e.getMessage());
429. return false;
430. }
431. if (alv.getCount() == 0)
432. {
433. // 没有item的时候也可以下拉刷新
434. true;
435. else if (alv.getFirstVisiblePosition() == 0 && alv.getChildAt(0).getTop() >= 0)
436. {
437. // 滑到AbsListView的顶部了
438. true;
439. else
440. false;
441. return false;
442. }
443. }
代码中的注释已经写的很清楚了。
既然PullToRefreshLayout已经写好了,接下来就来使用这个Layout实现下拉刷新~
首先得写个OnRefreshListener接口来回调刷新操作:
[java] view plain copy
1. public interface OnRefreshListener {
2. void onRefresh();
3. }
就一个刷新操作的方法,待会儿让Activity实现这个接口就可以在Activity中执行刷新操作了。
看一下MainActivity的布局:
[html] view plain copy
1. <com.jingchen.pulltorefresh.PullToRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
2. android:id="@+id/refresh_view"
3. android:layout_width="match_parent"
4. android:layout_height="match_parent" >
5.
6. <include layout="@layout/refresh_head" />
7.
8. <!-- 支持AbsListView的所有子类 -->
9. <ListView
10. android:id="@+id/content_view"
11. android:layout_width="match_parent"
12. android:layout_height="match_parent"
13. android:background="@color/white"
14. android:divider="@color/gray"
15. android:dividerHeight="1dp" >
16. </ListView>
17.
18. </com.jingchen.pulltorefresh.PullToRefreshLayout>
PullToRefreshLayout只能包含两个子控件:refresh_head和content_view。
看一下refresh_head的布局:
[html] view plain copy
1. <?xml version="1.0" encoding="utf-8"?>
2. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
3. android:id="@+id/head_view"
4. android:layout_width="match_parent"
5. android:layout_height="match_parent"
6. android:background="@color/light_blue" >
7.
8. <RelativeLayout
9. android:layout_width="match_parent"
10. android:layout_height="wrap_content"
11. android:layout_alignParentBottom="true"
12. android:paddingBottom="20dp"
13. android:paddingTop="20dp" >
14.
15. <RelativeLayout
16. android:layout_width="match_parent"
17. android:layout_height="wrap_content"
18. android:layout_centerInParent="true" >
19.
20. <ImageView
21. android:id="@+id/pull_icon"
22. android:layout_width="wrap_content"
23. android:layout_height="wrap_content"
24. android:layout_centerVertical="true"
25. android:layout_marginLeft="60dp"
26. android:background="@drawable/pull_icon_big" />
27.
28. <ImageView
29. android:id="@+id/refreshing_icon"
30. android:layout_width="wrap_content"
31. android:layout_height="wrap_content"
32. android:layout_centerVertical="true"
33. android:layout_marginLeft="60dp"
34. android:background="@drawable/refreshing"
35. android:visibility="gone" />
36.
37. <TextView
38. android:id="@+id/state_tv"
39. android:layout_width="wrap_content"
40. android:layout_height="wrap_content"
41. android:layout_centerInParent="true"
42. android:text="@string/pull_to_refresh"
43. android:textColor="@color/white"
44. android:textSize="16sp" />
45.
46. <ImageView
47. android:id="@+id/state_iv"
48. android:layout_width="wrap_content"
49. android:layout_height="wrap_content"
50. android:layout_centerVertical="true"
51. android:layout_marginRight="8dp"
52. android:layout_toLeftOf="@id/state_tv"
53. android:visibility="gone" />
54. </RelativeLayout>
55. </RelativeLayout>
56.
57. </RelativeLayout>
可以根据需要修改refresh_head的布局然后在PullToRefreshLayout中处理,但是相关View的id要和PullToRefreshLayout中用到的保持同步!
接下来是MainActivity的代码:
[java] view plain copy
1. package com.jingchen.pulltorefresh;
2.
3. import java.util.ArrayList;
4. import java.util.List;
5.
6. import android.app.Activity;
7. import android.content.Context;
8. import android.os.Bundle;
9. import android.os.Handler;
10. import android.os.Message;
11. import android.support.v4.view.PagerAdapter;
12. import android.support.v4.view.ViewPager;
13. import android.support.v4.view.ViewPager.OnPageChangeListener;
14. import android.view.LayoutInflater;
15. import android.view.View;
16. import android.view.View.OnClickListener;
17. import android.view.ViewGroup;
18. import android.view.animation.AnimationUtils;
19. import android.view.animation.LinearInterpolator;
20. import android.view.animation.RotateAnimation;
21. import android.widget.AbsListView;
22. import android.widget.AdapterView;
23. import android.widget.AdapterView.OnItemClickListener;
24. import android.widget.AdapterView.OnItemLongClickListener;
25. import android.widget.BaseExpandableListAdapter;
26. import android.widget.ExpandableListView;
27. import android.widget.ExpandableListView.OnChildClickListener;
28. import android.widget.ExpandableListView.OnGroupClickListener;
29. import android.widget.ListView;
30. import android.widget.TextView;
31. import android.widget.Toast;
32.
33. /**
34. * 除了下拉刷新,在contenview为ListView的情况下我给ListView增加了FooterView,实现点击加载更多
35. *
36. * @author 陈靖
37. *
38. */
39. public class MainActivity extends Activity implements OnRefreshListener, OnClickListener
40. {
41. private AbsListView alv;
42. private PullToRefreshLayout refreshLayout;
43. private View loading;
44. private RotateAnimation loadingAnimation;
45. private TextView loadTextView;
46. private MyAdapter adapter;
47. private boolean isLoading = false;
48.
49. @Override
50. protected void onCreate(Bundle savedInstanceState)
51. {
52. super.onCreate(savedInstanceState);
53. setContentView(R.layout.activity_main);
54. init();
55. }
56.
57. private void init()
58. {
59. alv = (AbsListView) findViewById(R.id.content_view);
60. refreshLayout = (PullToRefreshLayout) findViewById(R.id.refresh_view);
61. this);
62. initListView();
63.
64. this, R.anim.rotating);
65. // 添加匀速转动动画
66. new LinearInterpolator();
67. loadingAnimation.setInterpolator(lir);
68. }
69.
70. /**
71. * ListView初始化方法
72. */
73. private void initListView()
74. {
75. new ArrayList<String>();
76. for (int i = 0; i < 30; i++)
77. {
78. "这里是item " + i);
79. }
80. // 添加head
81. null);
82. null, false);
83. // 添加footer
84. null);
85. loading = footerView.findViewById(R.id.loading_icon);
86. loadTextView = (TextView) footerView.findViewById(R.id.loadmore_tv);
87. null, false);
88. this);
89. new MyAdapter(this, items);
90. alv.setAdapter(adapter);
91. new OnItemLongClickListener()
92. {
93.
94. @Override
95. public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id)
96. {
97. this, "LongClick on " + parent.getAdapter().getItemId(position), Toast.LENGTH_SHORT).show();
98. return true;
99. }
100. });
101. new OnItemClickListener()
102. {
103.
104. @Override
105. public void onItemClick(AdapterView<?> parent, View view, int position, long id)
106. {
107. this, " Click on " + parent.getAdapter().getItemId(position), Toast.LENGTH_SHORT).show();
108. }
109. });
110. }
111.
112. /**
113. * GridView初始化方法
114. */
115. private void initGridView()
116. {
117. new ArrayList<String>();
118. for (int i = 0; i < 30; i++)
119. {
120. "这里是item " + i);
121. }
122. new MyAdapter(this, items);
123. alv.setAdapter(adapter);
124. new OnItemLongClickListener()
125. {
126.
127. @Override
128. public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id)
129. {
130. this, "LongClick on " + parent.getAdapter().getItemId(position), Toast.LENGTH_SHORT).show();
131. return true;
132. }
133. });
134. new OnItemClickListener()
135. {
136.
137. @Override
138. public void onItemClick(AdapterView<?> parent, View view, int position, long id)
139. {
140. this, " Click on " + parent.getAdapter().getItemId(position), Toast.LENGTH_SHORT).show();
141. }
142. });
143. }
144.
145. /**
146. * ExpandableListView初始化方法
147. */
148. private void initExpandableListView()
149. {
150. new ExpandableListAdapter(this));
151. new OnChildClickListener()
152. {
153.
154. @Override
155. public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id)
156. {
157. this, " Click on group " + groupPosition + " item " + childPosition, Toast.LENGTH_SHORT).show();
158. return true;
159. }
160. });
161. new OnItemLongClickListener()
162. {
163.
164. @Override
165. public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id)
166. {
167. this, "LongClick on " + parent.getAdapter().getItemId(position), Toast.LENGTH_SHORT).show();
168. return true;
169. }
170. });
171. new OnGroupClickListener()
172. {
173.
174. @Override
175. public boolean onGroupClick(ExpandableListView parent, View v, int groupPosition, long id)
176. {
177. if (parent.isGroupExpanded(groupPosition))
178. {
179. // 如果展开则关闭
180. parent.collapseGroup(groupPosition);
181. else
182. {
183. // 如果关闭则打开,注意这里是手动打开不要默认滚动否则会有bug
184. parent.expandGroup(groupPosition);
185. }
186. return true;
187. }
188. });
189. }
190.
191. @Override
192. public void onRefresh()
193. {
194. // 下拉刷新操作
195. new Handler()
196. {
197. @Override
198. public void handleMessage(Message msg)
199. {
200. refreshLayout.refreshFinish(PullToRefreshLayout.REFRESH_SUCCEED);
201. }
202. 0, 5000);
203. }
204.
205. @Override
206. public void onClick(View v)
207. {
208. switch (v.getId())
209. {
210. case R.id.loadmore_layout:
211. if (!isLoading)
212. {
213. loading.setVisibility(View.VISIBLE);
214. loading.startAnimation(loadingAnimation);
215. loadTextView.setText(R.string.loading);
216. true;
217. }
218. break;
219. default:
220. break;
221. }
222.
223. }
224.
225. class ExpandableListAdapter extends BaseExpandableListAdapter
226. {
227. private String[] groupsStrings;// = new String[] { "这里是group 0",
228. // "这里是group 1", "这里是group 2" };
229. private String[][] groupItems;
230. private Context context;
231.
232. public ExpandableListAdapter(Context context)
233. {
234. this.context = context;
235. new String[8];
236. for (int i = 0; i < groupsStrings.length; i++)
237. {
238. new String("这里是group " + i);
239. }
240. new String[8][8];
241. for (int i = 0; i < groupItems.length; i++)
242. for (int j = 0; j < groupItems[i].length; j++)
243. {
244. new String("这里是group " + i + "里的item " + j);
245. }
246. }
247.
248. @Override
249. public int getGroupCount()
250. {
251. return groupsStrings.length;
252. }
253.
254. @Override
255. public int getChildrenCount(int groupPosition)
256. {
257. return groupItems[groupPosition].length;
258. }
259.
260. @Override
261. public Object getGroup(int groupPosition)
262. {
263. return groupsStrings[groupPosition];
264. }
265.
266. @Override
267. public Object getChild(int groupPosition, int childPosition)
268. {
269. return groupItems[groupPosition][childPosition];
270. }
271.
272. @Override
273. public long getGroupId(int groupPosition)
274. {
275. return groupPosition;
276. }
277.
278. @Override
279. public long getChildId(int groupPosition, int childPosition)
280. {
281. return childPosition;
282. }
283.
284. @Override
285. public boolean hasStableIds()
286. {
287. return true;
288. }
289.
290. @Override
291. public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent)
292. {
293. null);
294. TextView tv = (TextView) view.findViewById(R.id.name_tv);
295. tv.setText(groupsStrings[groupPosition]);
296. return view;
297. }
298.
299. @Override
300. public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent)
301. {
302. null);
303. TextView tv = (TextView) view.findViewById(R.id.name_tv);
304. tv.setText(groupItems[groupPosition][childPosition]);
305. return view;
306. }
307.
308. @Override
309. public boolean isChildSelectable(int groupPosition, int childPosition)
310. {
311. return true;
312. }
313.
314. }
315.
316. }
1. package com.jingchen.pulltorefresh;
2.
3. import java.util.ArrayList;
4. import java.util.List;
5.
6. import android.app.Activity;
7. import android.content.Context;
8. import android.os.Bundle;
9. import android.os.Handler;
10. import android.os.Message;
11. import android.support.v4.view.PagerAdapter;
12. import android.support.v4.view.ViewPager;
13. import android.support.v4.view.ViewPager.OnPageChangeListener;
14. import android.view.LayoutInflater;
15. import android.view.View;
16. import android.view.View.OnClickListener;
17. import android.view.ViewGroup;
18. import android.view.animation.AnimationUtils;
19. import android.view.animation.LinearInterpolator;
20. import android.view.animation.RotateAnimation;
21. import android.widget.AbsListView;
22. import android.widget.AdapterView;
23. import android.widget.AdapterView.OnItemClickListener;
24. import android.widget.AdapterView.OnItemLongClickListener;
25. import android.widget.BaseExpandableListAdapter;
26. import android.widget.ExpandableListView;
27. import android.widget.ExpandableListView.OnChildClickListener;
28. import android.widget.ExpandableListView.OnGroupClickListener;
29. import android.widget.ListView;
30. import android.widget.TextView;
31. import android.widget.Toast;
32.
33. /**
34. * 除了下拉刷新,在contenview为ListView的情况下我给ListView增加了FooterView,实现点击加载更多
35. *
36. * @author 陈靖
37. *
38. */
39. public class MainActivity extends Activity implements OnRefreshListener, OnClickListener
40. {
41. private AbsListView alv;
42. private PullToRefreshLayout refreshLayout;
43. private View loading;
44. private RotateAnimation loadingAnimation;
45. private TextView loadTextView;
46. private MyAdapter adapter;
47. private boolean isLoading = false;
48.
49. @Override
50. protected void onCreate(Bundle savedInstanceState)
51. {
52. super.onCreate(savedInstanceState);
53. setContentView(R.layout.activity_main);
54. init();
55. }
56.
57. private void init()
58. {
59. alv = (AbsListView) findViewById(R.id.content_view);
60. refreshLayout = (PullToRefreshLayout) findViewById(R.id.refresh_view);
61. this);
62. initListView();
63.
64. this, R.anim.rotating);
65. // 添加匀速转动动画
66. new LinearInterpolator();
67. loadingAnimation.setInterpolator(lir);
68. }
69.
70. /**
71. * ListView初始化方法
72. */
73. private void initListView()
74. {
75. new ArrayList<String>();
76. for (int i = 0; i < 30; i++)
77. {
78. "这里是item " + i);
79. }
80. // 添加head
81. null);
82. null, false);
83. // 添加footer
84. null);
85. loading = footerView.findViewById(R.id.loading_icon);
86. loadTextView = (TextView) footerView.findViewById(R.id.loadmore_tv);
87. null, false);
88. this);
89. new MyAdapter(this, items);
90. alv.setAdapter(adapter);
91. new OnItemLongClickListener()
92. {
93.
94. @Override
95. public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id)
96. {
97. this, "LongClick on " + parent.getAdapter().getItemId(position), Toast.LENGTH_SHORT).show();
98. return true;
99. }
100. });
101. new OnItemClickListener()
102. {
103.
104. @Override
105. public void onItemClick(AdapterView<?> parent, View view, int position, long id)
106. {
107. this, " Click on " + parent.getAdapter().getItemId(position), Toast.LENGTH_SHORT).show();
108. }
109. });
110. }
111.
112. /**
113. * GridView初始化方法
114. */
115. private void initGridView()
116. {
117. new ArrayList<String>();
118. for (int i = 0; i < 30; i++)
119. {
120. "这里是item " + i);
121. }
122. new MyAdapter(this, items);
123. alv.setAdapter(adapter);
124. new OnItemLongClickListener()
125. {
126.
127. @Override
128. public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id)
129. {
130. this, "LongClick on " + parent.getAdapter().getItemId(position), Toast.LENGTH_SHORT).show();
131. return true;
132. }
133. });
134. new OnItemClickListener()
135. {
136.
137. @Override
138. public void onItemClick(AdapterView<?> parent, View view, int position, long id)
139. {
140. this, " Click on " + parent.getAdapter().getItemId(position), Toast.LENGTH_SHORT).show();
141. }
142. });
143. }
144.
145. /**
146. * ExpandableListView初始化方法
147. */
148. private void initExpandableListView()
149. {
150. new ExpandableListAdapter(this));
151. new OnChildClickListener()
152. {
153.
154. @Override
155. public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id)
156. {
157. this, " Click on group " + groupPosition + " item " + childPosition, Toast.LENGTH_SHORT).show();
158. return true;
159. }
160. });
161. new OnItemLongClickListener()
162. {
163.
164. @Override
165. public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id)
166. {
167. this, "LongClick on " + parent.getAdapter().getItemId(position), Toast.LENGTH_SHORT).show();
168. return true;
169. }
170. });
171. new OnGroupClickListener()
172. {
173.
174. @Override
175. public boolean onGroupClick(ExpandableListView parent, View v, int groupPosition, long id)
176. {
177. if (parent.isGroupExpanded(groupPosition))
178. {
179. // 如果展开则关闭
180. parent.collapseGroup(groupPosition);
181. else
182. {
183. // 如果关闭则打开,注意这里是手动打开不要默认滚动否则会有bug
184. parent.expandGroup(groupPosition);
185. }
186. return true;
187. }
188. });
189. }
190.
191. @Override
192. public void onRefresh()
193. {
194. // 下拉刷新操作
195. new Handler()
196. {
197. @Override
198. public void handleMessage(Message msg)
199. {
200. refreshLayout.refreshFinish(PullToRefreshLayout.REFRESH_SUCCEED);
201. }
202. 0, 5000);
203. }
204.
205. @Override
206. public void onClick(View v)
207. {
208. switch (v.getId())
209. {
210. case R.id.loadmore_layout:
211. if (!isLoading)
212. {
213. loading.setVisibility(View.VISIBLE);
214. loading.startAnimation(loadingAnimation);
215. loadTextView.setText(R.string.loading);
216. true;
217. }
218. break;
219. default:
220. break;
221. }
222.
223. }
224.
225. class ExpandableListAdapter extends BaseExpandableListAdapter
226. {
227. private String[] groupsStrings;// = new String[] { "这里是group 0",
228. // "这里是group 1", "这里是group 2" };
229. private String[][] groupItems;
230. private Context context;
231.
232. public ExpandableListAdapter(Context context)
233. {
234. this.context = context;
235. new String[8];
236. for (int i = 0; i < groupsStrings.length; i++)
237. {
238. new String("这里是group " + i);
239. }
240. new String[8][8];
241. for (int i = 0; i < groupItems.length; i++)
242. for (int j = 0; j < groupItems[i].length; j++)
243. {
244. new String("这里是group " + i + "里的item " + j);
245. }
246. }
247.
248. @Override
249. public int getGroupCount()
250. {
251. return groupsStrings.length;
252. }
253.
254. @Override
255. public int getChildrenCount(int groupPosition)
256. {
257. return groupItems[groupPosition].length;
258. }
259.
260. @Override
261. public Object getGroup(int groupPosition)
262. {
263. return groupsStrings[groupPosition];
264. }
265.
266. @Override
267. public Object getChild(int groupPosition, int childPosition)
268. {
269. return groupItems[groupPosition][childPosition];
270. }
271.
272. @Override
273. public long getGroupId(int groupPosition)
274. {
275. return groupPosition;
276. }
277.
278. @Override
279. public long getChildId(int groupPosition, int childPosition)
280. {
281. return childPosition;
282. }
283.
284. @Override
285. public boolean hasStableIds()
286. {
287. return true;
288. }
289.
290. @Override
291. public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent)
292. {
293. null);
294. TextView tv = (TextView) view.findViewById(R.id.name_tv);
295. tv.setText(groupsStrings[groupPosition]);
296. return view;
297. }
298.
299. @Override
300. public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent)
301. {
302. null);
303. TextView tv = (TextView) view.findViewById(R.id.name_tv);
304. tv.setText(groupItems[groupPosition][childPosition]);
305. return view;
306. }
307.
308. @Override
309. public boolean isChildSelectable(int groupPosition, int childPosition)
310. {
311. return true;
312. }
313.
314. }
315.
316. }
在MainActivity中判断contentView是ListView的话给ListView添加了FooterView实现点击加载更多的功能。这只是一个演示PullToRefreshLayout使用的demo,可以参照一下修改。我已经在里面写了ListView,GridView和ExpandableListView的初始化方法,根据自己使用的是哪个来调用吧。那么这是ListView的下拉刷新和加载更多。如果我要GridView也有下拉刷新功能呢?那就把MainActivity的布局换成这样:
[html] view plain copy
1. <com.jingchen.pulltorefresh.PullToRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
2. android:id="@+id/refresh_view"
3. android:layout_width="match_parent"
4. android:layout_height="match_parent" >
5.
6. <include layout="@layout/refresh_head" />
7. <!-- 支持AbsListView的所有子类 -->
8. <GridView
9. android:id="@+id/content_view"
10. android:layout_width="match_parent"
11. android:layout_height="match_parent"
12. android:background="@color/white"
13. android:columnWidth="90dp"
14. android:gravity="center"
15. android:horizontalSpacing="10dp"
16. android:numColumns="auto_fit"
17. android:stretchMode="columnWidth"
18. android:verticalSpacing="15dp" >
19. </GridView>
20.
21. </com.jingchen.pulltorefresh.PullToRefreshLayout>
如果是ExpandableListView则把布局改成这样:
[html] view plain copy
1. <com.jingchen.pulltorefresh.PullToRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
2. android:id="@+id/refresh_view"
3. android:layout_width="match_parent"
4. android:layout_height="match_parent" >
5.
6. <include layout="@layout/refresh_head" />
7. <!-- 支持AbsListView的所有子类 -->
8. <ExpandableListView
9. android:id="@+id/content_view"
10. android:layout_width="match_parent"
11. android:layout_height="match_parent"
12. android:background="@color/white" >
13. </ExpandableListView>
14.
15. </com.jingchen.pulltorefresh.PullToRefreshLayout>
怎么样?很简单吧?简单易用,不用再去继承修改了~