├── .gitignore
├── ic_launcher-web.png
├── libs
└── android-support-v4.jar
├── res
├── drawable-hdpi
│ └── ic_launcher.png
├── drawable-mdpi
│ └── ic_launcher.png
├── drawable-xhdpi
│ └── ic_launcher.png
├── drawable-xxhdpi
│ └── ic_launcher.png
├── drawable
│ └── umeng_socialize_pulltorefresh_arrow.png
├── values
│ ├── colors.xml
│ ├── strings.xml
│ └── styles.xml
└── layout
│ ├── pull_to_refresh_footer.xml
│ └── pull_to_refresh_header.xml
├── android_my_pull_demo
├── ic_launcher-web.png
├── res
│ ├── drawable
│ │ └── icon.png
│ ├── drawable-hdpi
│ │ └── ic_launcher.png
│ ├── drawable-mdpi
│ │ └── ic_launcher.png
│ ├── drawable-xhdpi
│ │ └── ic_launcher.png
│ ├── drawable-xxhdpi
│ │ └── ic_launcher.png
│ ├── values
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── layout
│ │ ├── test.xml
│ │ ├── slide_item_layout.xml
│ │ ├── swipe_lv_layout.xml
│ │ ├── pull_refresh_main.xml
│ │ └── main.xml
├── libs
│ ├── android-support-v4.jar
│ └── android_common_adapter.jar
├── project.properties
├── proguard-project.txt
├── AndroidManifest.xml
└── src
│ └── com
│ └── example
│ └── pull
│ ├── MainActivity.java
│ └── ShowActivity.java
├── AndroidManifest.xml
├── project.properties
├── proguard-project.txt
├── src
└── com
│ └── uit
│ └── pullrefresh
│ ├── listener
│ ├── OnLoadListener.java
│ └── OnRefreshListener.java
│ ├── swipe
│ ├── RefreshGvLayout.java
│ ├── RefreshLvLayout.java
│ └── RefreshLayout.java
│ ├── scroller
│ ├── RefreshAdaterView.java
│ ├── impl
│ │ ├── RefreshTextView.java
│ │ ├── RefreshGridView.java
│ │ ├── RefreshListView.java
│ │ └── RefreshSlideDeleteListView.java
│ └── RefreshLayoutBase.java
│ └── base
│ ├── impl
│ ├── PullRefreshTextView.java
│ └── PullRefreshListView.java
│ └── PullRefreshBase.java
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | *.project
2 | *.classpath
3 | bin/
4 | gen/
5 | *.DS_Store
6 | */bin/
7 | */gen/
8 | .settings/
9 |
--------------------------------------------------------------------------------
/ic_launcher-web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hehonghui/android_my_pull_refresh_view/HEAD/ic_launcher-web.png
--------------------------------------------------------------------------------
/libs/android-support-v4.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hehonghui/android_my_pull_refresh_view/HEAD/libs/android-support-v4.jar
--------------------------------------------------------------------------------
/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hehonghui/android_my_pull_refresh_view/HEAD/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hehonghui/android_my_pull_refresh_view/HEAD/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hehonghui/android_my_pull_refresh_view/HEAD/res/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hehonghui/android_my_pull_refresh_view/HEAD/res/drawable-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android_my_pull_demo/ic_launcher-web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hehonghui/android_my_pull_refresh_view/HEAD/android_my_pull_demo/ic_launcher-web.png
--------------------------------------------------------------------------------
/android_my_pull_demo/res/drawable/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hehonghui/android_my_pull_refresh_view/HEAD/android_my_pull_demo/res/drawable/icon.png
--------------------------------------------------------------------------------
/android_my_pull_demo/libs/android-support-v4.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hehonghui/android_my_pull_refresh_view/HEAD/android_my_pull_demo/libs/android-support-v4.jar
--------------------------------------------------------------------------------
/android_my_pull_demo/libs/android_common_adapter.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hehonghui/android_my_pull_refresh_view/HEAD/android_my_pull_demo/libs/android_common_adapter.jar
--------------------------------------------------------------------------------
/res/drawable/umeng_socialize_pulltorefresh_arrow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hehonghui/android_my_pull_refresh_view/HEAD/res/drawable/umeng_socialize_pulltorefresh_arrow.png
--------------------------------------------------------------------------------
/android_my_pull_demo/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hehonghui/android_my_pull_refresh_view/HEAD/android_my_pull_demo/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android_my_pull_demo/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hehonghui/android_my_pull_refresh_view/HEAD/android_my_pull_demo/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android_my_pull_demo/res/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hehonghui/android_my_pull_refresh_view/HEAD/android_my_pull_demo/res/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android_my_pull_demo/res/drawable-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hehonghui/android_my_pull_refresh_view/HEAD/android_my_pull_demo/res/drawable-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | #33b5e5
6 | #ff4444
7 | #99cc00
8 | #ffbb33
9 |
10 |
--------------------------------------------------------------------------------
/android_my_pull_demo/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | android_my_pull_demo
4 | listview下拉刷新
5 | GridView下拉刷新
6 | TextView下拉刷新
7 | 滑动删除下拉刷新
8 | SwipeRefreshLayout下拉刷新
9 | 删除
10 |
11 |
--------------------------------------------------------------------------------
/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
9 |
10 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/project.properties:
--------------------------------------------------------------------------------
1 | # This file is automatically generated by Android Tools.
2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED!
3 | #
4 | # This file must be checked in Version Control Systems.
5 | #
6 | # To customize properties used by the Ant build system edit
7 | # "ant.properties", and override values to adapt the script to your
8 | # project structure.
9 | #
10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
11 | #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
12 |
13 | # Project target.
14 | target=android-10
15 | android.library=true
16 |
--------------------------------------------------------------------------------
/android_my_pull_demo/project.properties:
--------------------------------------------------------------------------------
1 | # This file is automatically generated by Android Tools.
2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED!
3 | #
4 | # This file must be checked in Version Control Systems.
5 | #
6 | # To customize properties used by the Ant build system edit
7 | # "ant.properties", and override values to adapt the script to your
8 | # project structure.
9 | #
10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
11 | #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
12 |
13 | # Project target.
14 | target=android-10
15 | android.library.reference.1=..
16 |
--------------------------------------------------------------------------------
/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | android_my_pull_refresh
4 | 点击刷新
5 | 下拉刷新
6 | 松开可以刷新
7 | 加载中…
8 | 上次更新时间
9 | 上拉加载更多
10 | 加载更多
11 |
12 |
--------------------------------------------------------------------------------
/android_my_pull_demo/res/layout/test.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
14 |
15 |
20 |
21 |
--------------------------------------------------------------------------------
/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
14 |
15 |
16 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/android_my_pull_demo/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
14 |
15 |
16 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/android_my_pull_demo/res/layout/slide_item_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
15 |
16 |
22 |
23 |
--------------------------------------------------------------------------------
/proguard-project.txt:
--------------------------------------------------------------------------------
1 | # To enable ProGuard in your project, edit project.properties
2 | # to define the proguard.config property as described in that file.
3 | #
4 | # Add project specific ProGuard rules here.
5 | # By default, the flags in this file are appended to flags specified
6 | # in ${sdk.dir}/tools/proguard/proguard-android.txt
7 | # You can edit the include path and order by changing the ProGuard
8 | # include property in project.properties.
9 | #
10 | # For more details, see
11 | # http://developer.android.com/guide/developing/tools/proguard.html
12 |
13 | # Add any project specific keep options here:
14 |
15 | # If your project uses WebView with JS, uncomment the following
16 | # and specify the fully qualified class name to the JavaScript interface
17 | # class:
18 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
19 | # public *;
20 | #}
21 |
--------------------------------------------------------------------------------
/android_my_pull_demo/proguard-project.txt:
--------------------------------------------------------------------------------
1 | # To enable ProGuard in your project, edit project.properties
2 | # to define the proguard.config property as described in that file.
3 | #
4 | # Add project specific ProGuard rules here.
5 | # By default, the flags in this file are appended to flags specified
6 | # in ${sdk.dir}/tools/proguard/proguard-android.txt
7 | # You can edit the include path and order by changing the ProGuard
8 | # include property in project.properties.
9 | #
10 | # For more details, see
11 | # http://developer.android.com/guide/developing/tools/proguard.html
12 |
13 | # Add any project specific keep options here:
14 |
15 | # If your project uses WebView with JS, uncomment the following
16 | # and specify the fully qualified class name to the JavaScript interface
17 | # class:
18 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
19 | # public *;
20 | #}
21 |
--------------------------------------------------------------------------------
/android_my_pull_demo/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
9 |
10 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/android_my_pull_demo/res/layout/swipe_lv_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
12 |
13 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/res/layout/pull_to_refresh_footer.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
17 |
18 |
30 |
31 |
--------------------------------------------------------------------------------
/android_my_pull_demo/res/layout/pull_refresh_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
14 |
15 |
20 |
21 |
26 |
27 |
32 |
33 |
38 |
39 |
--------------------------------------------------------------------------------
/src/com/uit/pullrefresh/listener/OnLoadListener.java:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * created by Mr.Simple, Sep 10, 20146:14:22 PM.
4 | * Copyright (c) 2014, hehonghui@umeng.com All Rights Reserved.
5 | *
6 | * #####################################################
7 | * # #
8 | * # _oo0oo_ #
9 | * # o8888888o #
10 | * # 88" . "88 #
11 | * # (| -_- |) #
12 | * # 0\ = /0 #
13 | * # ___/`---'\___ #
14 | * # .' \\| |# '. #
15 | * # / \\||| : |||# \ #
16 | * # / _||||| -:- |||||- \ #
17 | * # | | \\\ - #/ | | #
18 | * # | \_| ''\---/'' |_/ | #
19 | * # \ .-\__ '-' ___/-. / #
20 | * # ___'. .' /--.--\ `. .'___ #
21 | * # ."" '< `.___\_<|>_/___.' >' "". #
22 | * # | | : `- \`.;`\ _ /`;.`/ - ` : | | #
23 | * # \ \ `_. \_ __\ /__ _/ .-` / / #
24 | * # =====`-.____`.___ \_____/___.-`___.-'===== #
25 | * # `=---=' #
26 | * # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #
27 | * # #
28 | * # 佛祖保佑 永无BUG #
29 | * # #
30 | * #####################################################
31 | */
32 |
33 | package com.uit.pullrefresh.listener;
34 |
35 | /**
36 | * @author mrsimple
37 | */
38 | public interface OnLoadListener {
39 | public void onLoadMore();
40 | }
41 |
--------------------------------------------------------------------------------
/src/com/uit/pullrefresh/listener/OnRefreshListener.java:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * created by Mr.Simple, Sep 10, 20146:12:35 PM.
4 | * Copyright (c) 2014, hehonghui@umeng.com All Rights Reserved.
5 | *
6 | * #####################################################
7 | * # #
8 | * # _oo0oo_ #
9 | * # o8888888o #
10 | * # 88" . "88 #
11 | * # (| -_- |) #
12 | * # 0\ = /0 #
13 | * # ___/`---'\___ #
14 | * # .' \\| |# '. #
15 | * # / \\||| : |||# \ #
16 | * # / _||||| -:- |||||- \ #
17 | * # | | \\\ - #/ | | #
18 | * # | \_| ''\---/'' |_/ | #
19 | * # \ .-\__ '-' ___/-. / #
20 | * # ___'. .' /--.--\ `. .'___ #
21 | * # ."" '< `.___\_<|>_/___.' >' "". #
22 | * # | | : `- \`.;`\ _ /`;.`/ - ` : | | #
23 | * # \ \ `_. \_ __\ /__ _/ .-` / / #
24 | * # =====`-.____`.___ \_____/___.-`___.-'===== #
25 | * # `=---=' #
26 | * # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #
27 | * # #
28 | * # 佛祖保佑 永无BUG #
29 | * # #
30 | * #####################################################
31 | */
32 |
33 | package com.uit.pullrefresh.listener;
34 |
35 | /**
36 | * @author mrsimple
37 | */
38 | public interface OnRefreshListener {
39 | public void onRefresh();
40 | }
41 |
--------------------------------------------------------------------------------
/res/layout/pull_to_refresh_header.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
18 |
19 |
29 |
30 |
42 |
43 |
53 |
54 |
--------------------------------------------------------------------------------
/src/com/uit/pullrefresh/swipe/RefreshGvLayout.java:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * created by Mr.Simple, Oct 29, 201411:50:58 AM.
4 | * Copyright (c) 2014, hehonghui@umeng.com All Rights Reserved.
5 | *
6 | * #####################################################
7 | * # #
8 | * # _oo0oo_ #
9 | * # o8888888o #
10 | * # 88" . "88 #
11 | * # (| -_- |) #
12 | * # 0\ = /0 #
13 | * # ___/`---'\___ #
14 | * # .' \\| |# '. #
15 | * # / \\||| : |||# \ #
16 | * # / _||||| -:- |||||- \ #
17 | * # | | \\\ - #/ | | #
18 | * # | \_| ''\---/'' |_/ | #
19 | * # \ .-\__ '-' ___/-. / #
20 | * # ___'. .' /--.--\ `. .'___ #
21 | * # ."" '< `.___\_<|>_/___.' >' "". #
22 | * # | | : `- \`.;`\ _ /`;.`/ - ` : | | #
23 | * # \ \ `_. \_ __\ /__ _/ .-` / / #
24 | * # =====`-.____`.___ \_____/___.-`___.-'===== #
25 | * # `=---=' #
26 | * # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #
27 | * # #
28 | * # 佛祖保佑 永无BUG #
29 | * # #
30 | * #####################################################
31 | */
32 |
33 | package com.uit.pullrefresh.swipe;
34 |
35 | import android.content.Context;
36 | import android.util.AttributeSet;
37 | import android.widget.GridView;
38 |
39 | /**
40 | * @author mrsimple
41 | */
42 | public class RefreshGvLayout extends RefreshLayout {
43 |
44 | public RefreshGvLayout(Context context) {
45 | this(context, null);
46 | }
47 |
48 | public RefreshGvLayout(Context context, AttributeSet attrs) {
49 | super(context, attrs);
50 | }
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/src/com/uit/pullrefresh/scroller/RefreshAdaterView.java:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * created by Mr.Simple, Oct 1, 20141:42:43 PM.
4 | * Copyright (c) 2014, hehonghui@umeng.com All Rights Reserved.
5 | *
6 | * #####################################################
7 | * # #
8 | * # _oo0oo_ #
9 | * # o8888888o #
10 | * # 88" . "88 #
11 | * # (| -_- |) #
12 | * # 0\ = /0 #
13 | * # ___/`---'\___ #
14 | * # .' \\| |# '. #
15 | * # / \\||| : |||# \ #
16 | * # / _||||| -:- |||||- \ #
17 | * # | | \\\ - #/ | | #
18 | * # | \_| ''\---/'' |_/ | #
19 | * # \ .-\__ '-' ___/-. / #
20 | * # ___'. .' /--.--\ `. .'___ #
21 | * # ."" '< `.___\_<|>_/___.' >' "". #
22 | * # | | : `- \`.;`\ _ /`;.`/ - ` : | | #
23 | * # \ \ `_. \_ __\ /__ _/ .-` / / #
24 | * # =====`-.____`.___ \_____/___.-`___.-'===== #
25 | * # `=---=' #
26 | * # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #
27 | * # #
28 | * # 佛祖保佑 永无BUG #
29 | * # #
30 | * #####################################################
31 | */
32 |
33 | package com.uit.pullrefresh.scroller;
34 |
35 | import android.content.Context;
36 | import android.util.AttributeSet;
37 | import android.widget.AbsListView;
38 | import android.widget.ListAdapter;
39 |
40 | /**
41 | * @author mrsimple
42 | * @param
43 | */
44 | public abstract class RefreshAdaterView extends RefreshLayoutBase {
45 |
46 | public RefreshAdaterView(Context context) {
47 | this(context, null);
48 | }
49 |
50 | public RefreshAdaterView(Context context, AttributeSet attrs) {
51 | this(context, attrs, 0);
52 | }
53 |
54 | public RefreshAdaterView(Context context, AttributeSet attrs, int defStyle) {
55 | super(context, attrs, defStyle);
56 | }
57 |
58 | /**
59 | *
60 | */
61 | public void setAdapter(ListAdapter adapter) {
62 | mContentView.setAdapter(adapter);
63 | }
64 |
65 | public ListAdapter getAdapter() {
66 | return mContentView.getAdapter();
67 | }
68 |
69 | }
70 |
--------------------------------------------------------------------------------
/src/com/uit/pullrefresh/scroller/impl/RefreshTextView.java:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * created by Mr.Simple, Sep 30, 201411:38:09 PM.
4 | * Copyright (c) 2014, hehonghui@umeng.com All Rights Reserved.
5 | *
6 | * #####################################################
7 | * # #
8 | * # _oo0oo_ #
9 | * # o8888888o #
10 | * # 88" . "88 #
11 | * # (| -_- |) #
12 | * # 0\ = /0 #
13 | * # ___/`---'\___ #
14 | * # .' \\| |# '. #
15 | * # / \\||| : |||# \ #
16 | * # / _||||| -:- |||||- \ #
17 | * # | | \\\ - #/ | | #
18 | * # | \_| ''\---/'' |_/ | #
19 | * # \ .-\__ '-' ___/-. / #
20 | * # ___'. .' /--.--\ `. .'___ #
21 | * # ."" '< `.___\_<|>_/___.' >' "". #
22 | * # | | : `- \`.;`\ _ /`;.`/ - ` : | | #
23 | * # \ \ `_. \_ __\ /__ _/ .-` / / #
24 | * # =====`-.____`.___ \_____/___.-`___.-'===== #
25 | * # `=---=' #
26 | * # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #
27 | * # #
28 | * # 佛祖保佑 永无BUG #
29 | * # #
30 | * #####################################################
31 | */
32 |
33 | package com.uit.pullrefresh.scroller.impl;
34 |
35 | import android.content.Context;
36 | import android.util.AttributeSet;
37 | import android.widget.TextView;
38 |
39 | import com.uit.pullrefresh.scroller.RefreshLayoutBase;
40 |
41 | public class RefreshTextView extends RefreshLayoutBase {
42 |
43 | public RefreshTextView(Context context) {
44 | this(context, null);
45 | }
46 |
47 | /**
48 | * @param context
49 | * @param attrs
50 | */
51 | public RefreshTextView(Context context, AttributeSet attrs) {
52 | this(context, attrs, 0);
53 | }
54 |
55 | public RefreshTextView(Context context, AttributeSet attrs, int defStyle) {
56 | super(context, attrs, defStyle);
57 | }
58 |
59 | @Override
60 | protected void setupContentView(Context context) {
61 | mContentView = new TextView(context);
62 | // 开启可点击,使得onTouchEvent在处理触摸事件时返回true
63 | mContentView.setClickable(true);
64 | }
65 |
66 | @Override
67 | protected boolean isTop() {
68 | return true;
69 | }
70 |
71 | @Override
72 | protected boolean isBottom() {
73 | return false;
74 | }
75 |
76 | }
77 |
--------------------------------------------------------------------------------
/src/com/uit/pullrefresh/base/impl/PullRefreshTextView.java:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * created by Mr.Simple, Sep 13, 20141:34:49 PM.
4 | * Copyright (c) 2014, hehonghui@umeng.com All Rights Reserved.
5 | *
6 | * #####################################################
7 | * # #
8 | * # _oo0oo_ #
9 | * # o8888888o #
10 | * # 88" . "88 #
11 | * # (| -_- |) #
12 | * # 0\ = /0 #
13 | * # ___/`---'\___ #
14 | * # .' \\| |# '. #
15 | * # / \\||| : |||# \ #
16 | * # / _||||| -:- |||||- \ #
17 | * # | | \\\ - #/ | | #
18 | * # | \_| ''\---/'' |_/ | #
19 | * # \ .-\__ '-' ___/-. / #
20 | * # ___'. .' /--.--\ `. .'___ #
21 | * # ."" '< `.___\_<|>_/___.' >' "". #
22 | * # | | : `- \`.;`\ _ /`;.`/ - ` : | | #
23 | * # \ \ `_. \_ __\ /__ _/ .-` / / #
24 | * # =====`-.____`.___ \_____/___.-`___.-'===== #
25 | * # `=---=' #
26 | * # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #
27 | * # #
28 | * # 佛祖保佑 永无BUG #
29 | * # #
30 | * #####################################################
31 | */
32 |
33 | package com.uit.pullrefresh.base.impl;
34 |
35 | import android.content.Context;
36 | import android.graphics.Color;
37 | import android.util.AttributeSet;
38 | import android.view.Gravity;
39 | import android.widget.TextView;
40 |
41 | import com.uit.pullrefresh.base.PullRefreshBase;
42 |
43 | /**
44 | * @author mrsimple
45 | */
46 | public class PullRefreshTextView extends PullRefreshBase {
47 |
48 | public PullRefreshTextView(Context context) {
49 | super(context);
50 | }
51 |
52 | public PullRefreshTextView(Context context, AttributeSet attrs) {
53 | super(context, attrs);
54 | }
55 |
56 | @Override
57 | protected void initContentView() {
58 | // 初始化mContentView
59 | mContentView = new TextView(getContext());
60 | mContentView.setTextSize(20f);
61 | mContentView.setGravity(Gravity.CENTER);
62 | mContentView.setBackgroundColor(Color.CYAN);
63 | }
64 |
65 | /*
66 | * 是否滑动到了顶端,如果返回true, 则表示到了顶端,用户继续下拉则触发下拉刷新.由于TextView默认没有滑动,因此直接返回true.
67 | * @see com.uit.pullrefresh.base.PullRefreshBase#isTop()
68 | */
69 | @Override
70 | protected boolean isTop() {
71 | return true;
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/src/com/uit/pullrefresh/scroller/impl/RefreshGridView.java:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * created by Mr.Simple, Sep 30, 201411:32:22 PM.
4 | * Copyright (c) 2014, hehonghui@umeng.com All Rights Reserved.
5 | *
6 | * #####################################################
7 | * # #
8 | * # _oo0oo_ #
9 | * # o8888888o #
10 | * # 88" . "88 #
11 | * # (| -_- |) #
12 | * # 0\ = /0 #
13 | * # ___/`---'\___ #
14 | * # .' \\| |# '. #
15 | * # / \\||| : |||# \ #
16 | * # / _||||| -:- |||||- \ #
17 | * # | | \\\ - #/ | | #
18 | * # | \_| ''\---/'' |_/ | #
19 | * # \ .-\__ '-' ___/-. / #
20 | * # ___'. .' /--.--\ `. .'___ #
21 | * # ."" '< `.___\_<|>_/___.' >' "". #
22 | * # | | : `- \`.;`\ _ /`;.`/ - ` : | | #
23 | * # \ \ `_. \_ __\ /__ _/ .-` / / #
24 | * # =====`-.____`.___ \_____/___.-`___.-'===== #
25 | * # `=---=' #
26 | * # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #
27 | * # #
28 | * # 佛祖保佑 永无BUG #
29 | * # #
30 | * #####################################################
31 | */
32 |
33 | package com.uit.pullrefresh.scroller.impl;
34 |
35 | import android.content.Context;
36 | import android.util.AttributeSet;
37 | import android.widget.GridView;
38 |
39 | import com.uit.pullrefresh.scroller.RefreshAdaterView;
40 |
41 | /**
42 | * @author mrsimple
43 | */
44 | public class RefreshGridView extends RefreshAdaterView {
45 |
46 | public RefreshGridView(Context context) {
47 | this(context, null);
48 | }
49 |
50 | /**
51 | * @param context
52 | * @param attrs
53 | */
54 | public RefreshGridView(Context context, AttributeSet attrs) {
55 | this(context, attrs, 0);
56 | }
57 |
58 | public RefreshGridView(Context context, AttributeSet attrs, int defStyle) {
59 | super(context, attrs, defStyle);
60 | }
61 |
62 | @Override
63 | protected void setupContentView(Context context) {
64 | mContentView = new GridView(context);
65 | mContentView.setNumColumns(4);
66 | mContentView.setHorizontalSpacing(8);
67 | mContentView.setVerticalSpacing(8);
68 | // 设置滚动监听器
69 | mContentView.setOnScrollListener(this);
70 | }
71 |
72 | @Override
73 | protected boolean isTop() {
74 | return mContentView.getFirstVisiblePosition() == 0
75 | && getScrollY() <= mHeaderView.getMeasuredHeight();
76 | }
77 |
78 | @Override
79 | protected boolean isBottom() {
80 | return mContentView != null && mContentView.getAdapter() != null
81 | && mContentView.getLastVisiblePosition() ==
82 | mContentView.getAdapter().getCount() - 1 && mYOffset < 0;
83 | }
84 |
85 | }
86 |
--------------------------------------------------------------------------------
/src/com/uit/pullrefresh/scroller/impl/RefreshListView.java:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * created by Mr.Simple, Sep 30, 201411:13:20 PM.
4 | * Copyright (c) 2014, hehonghui@umeng.com All Rights Reserved.
5 | *
6 | * #####################################################
7 | * # #
8 | * # _oo0oo_ #
9 | * # o8888888o #
10 | * # 88" . "88 #
11 | * # (| -_- |) #
12 | * # 0\ = /0 #
13 | * # ___/`---'\___ #
14 | * # .' \\| |# '. #
15 | * # / \\||| : |||# \ #
16 | * # / _||||| -:- |||||- \ #
17 | * # | | \\\ - #/ | | #
18 | * # | \_| ''\---/'' |_/ | #
19 | * # \ .-\__ '-' ___/-. / #
20 | * # ___'. .' /--.--\ `. .'___ #
21 | * # ."" '< `.___\_<|>_/___.' >' "". #
22 | * # | | : `- \`.;`\ _ /`;.`/ - ` : | | #
23 | * # \ \ `_. \_ __\ /__ _/ .-` / / #
24 | * # =====`-.____`.___ \_____/___.-`___.-'===== #
25 | * # `=---=' #
26 | * # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #
27 | * # #
28 | * # 佛祖保佑 永无BUG #
29 | * # #
30 | * #####################################################
31 | */
32 |
33 | package com.uit.pullrefresh.scroller.impl;
34 |
35 | import android.content.Context;
36 | import android.util.AttributeSet;
37 | import android.widget.ListView;
38 |
39 | import com.uit.pullrefresh.scroller.RefreshAdaterView;
40 |
41 | /**
42 | * @author mrsimple
43 | */
44 | public class RefreshListView extends RefreshAdaterView {
45 |
46 | /**
47 | * @param context
48 | */
49 | public RefreshListView(Context context) {
50 | this(context, null);
51 | }
52 |
53 | /**
54 | * @param context
55 | * @param attrs
56 | */
57 | public RefreshListView(Context context, AttributeSet attrs) {
58 | this(context, attrs, 0);
59 | }
60 |
61 | /**
62 | * @param context
63 | * @param attrs
64 | * @param defStyle
65 | */
66 | public RefreshListView(Context context, AttributeSet attrs, int defStyle) {
67 | super(context, attrs, defStyle);
68 | }
69 |
70 | @Override
71 | protected void setupContentView(Context context) {
72 | mContentView = new ListView(context);
73 | // 设置滚动监听器
74 | mContentView.setOnScrollListener(this);
75 |
76 | }
77 |
78 | @Override
79 | protected boolean isTop() {
80 |
81 | // Log.d(VIEW_LOG_TAG,
82 | // "### first pos = " + mContentView.getFirstVisiblePosition()
83 | // + ", getScrollY= " + getScrollY());
84 | return mContentView.getFirstVisiblePosition() == 0
85 | && getScrollY() <= mHeaderView.getMeasuredHeight();
86 | }
87 |
88 | @Override
89 | protected boolean isBottom() {
90 | // Log.d(VIEW_LOG_TAG, "### last position = " +
91 | // contentView.getLastVisiblePosition()
92 | // + ", count = " + contentView.getAdapter().getCount());
93 | return mContentView != null && mContentView.getAdapter() != null
94 | && mContentView.getLastVisiblePosition() ==
95 | mContentView.getAdapter().getCount() - 1;
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/src/com/uit/pullrefresh/base/impl/PullRefreshListView.java:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * created by Mr.Simple, Sep 10, 20146:29:46 PM.
4 | * Copyright (c) 2014, hehonghui@umeng.com All Rights Reserved.
5 | *
6 | * #####################################################
7 | * # #
8 | * # _oo0oo_ #
9 | * # o8888888o #
10 | * # 88" . "88 #
11 | * # (| -_- |) #
12 | * # 0\ = /0 #
13 | * # ___/`---'\___ #
14 | * # .' \\| |# '. #
15 | * # / \\||| : |||# \ #
16 | * # / _||||| -:- |||||- \ #
17 | * # | | \\\ - #/ | | #
18 | * # | \_| ''\---/'' |_/ | #
19 | * # \ .-\__ '-' ___/-. / #
20 | * # ___'. .' /--.--\ `. .'___ #
21 | * # ."" '< `.___\_<|>_/___.' >' "". #
22 | * # | | : `- \`.;`\ _ /`;.`/ - ` : | | #
23 | * # \ \ `_. \_ __\ /__ _/ .-` / / #
24 | * # =====`-.____`.___ \_____/___.-`___.-'===== #
25 | * # `=---=' #
26 | * # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #
27 | * # #
28 | * # 佛祖保佑 永无BUG #
29 | * # #
30 | * #####################################################
31 | */
32 |
33 | package com.uit.pullrefresh.base.impl;
34 |
35 | import android.content.Context;
36 | import android.util.AttributeSet;
37 | import android.view.View;
38 | import android.widget.ListView;
39 |
40 | import com.uit.pullrefresh.base.PullRefreshBase;
41 |
42 | /**
43 | * @author mrsimple
44 | */
45 | public class PullRefreshListView extends PullRefreshBase {
46 |
47 | /**
48 | * @param context
49 | */
50 | public PullRefreshListView(Context context) {
51 | this(context, null);
52 | }
53 |
54 | /**
55 | * @param context
56 | * @param attrs
57 | */
58 | public PullRefreshListView(Context context, AttributeSet attrs) {
59 | super(context, attrs);
60 | }
61 |
62 | /*
63 | * 是否滑动到了顶端,如果返回true, 则表示到了顶端,用户继续下拉则触发下拉刷新
64 | * @see com.uit.pullrefresh.base.PullRefreshBase#isTop()
65 | */
66 | @Override
67 | protected boolean isTop() {
68 | View firstChild = mContentView.getChildAt(0);
69 | if (firstChild == null) {
70 | return true;
71 | }
72 | return mContentView.getFirstVisiblePosition() == 0
73 | && (firstChild.getTop() >= mContentView.getTop());
74 | }
75 |
76 | /*
77 | * 下拉到listview 最后一项时则返回true, 将出发自动加载
78 | * @see com.uit.pullrefresh.base.PullRefreshBase#isShowFooterView()
79 | */
80 | @Override
81 | protected boolean isShowFooterView() {
82 | if (mContentView == null || mContentView.getAdapter() == null) {
83 | return false;
84 | }
85 |
86 | return mContentView.getLastVisiblePosition() == mContentView.getAdapter().getCount() - 1;
87 | }
88 |
89 | /*
90 | * 初始化mContentView
91 | * @see com.uit.pullrefresh.base.PullRefreshBase#initContentView()
92 | */
93 | @Override
94 | protected void initContentView() {
95 | // 初始化mContentView
96 | mContentView = new ListView(getContext());
97 | // 设置OnScrollListener, 用以实现滑动到底部时的自动加载功能,如果不需要该功能可以不设置.
98 | mContentView.setOnScrollListener(this);
99 | }
100 |
101 | }
102 |
--------------------------------------------------------------------------------
/src/com/uit/pullrefresh/swipe/RefreshLvLayout.java:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * created by Mr.Simple, Oct 29, 201411:47:48 AM.
4 | * Copyright (c) 2014, hehonghui@umeng.com All Rights Reserved.
5 | *
6 | * #####################################################
7 | * # #
8 | * # _oo0oo_ #
9 | * # o8888888o #
10 | * # 88" . "88 #
11 | * # (| -_- |) #
12 | * # 0\ = /0 #
13 | * # ___/`---'\___ #
14 | * # .' \\| |# '. #
15 | * # / \\||| : |||# \ #
16 | * # / _||||| -:- |||||- \ #
17 | * # | | \\\ - #/ | | #
18 | * # | \_| ''\---/'' |_/ | #
19 | * # \ .-\__ '-' ___/-. / #
20 | * # ___'. .' /--.--\ `. .'___ #
21 | * # ."" '< `.___\_<|>_/___.' >' "". #
22 | * # | | : `- \`.;`\ _ /`;.`/ - ` : | | #
23 | * # \ \ `_. \_ __\ /__ _/ .-` / / #
24 | * # =====`-.____`.___ \_____/___.-`___.-'===== #
25 | * # `=---=' #
26 | * # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #
27 | * # #
28 | * # 佛祖保佑 永无BUG #
29 | * # #
30 | * #####################################################
31 | */
32 |
33 | package com.uit.pullrefresh.swipe;
34 |
35 | import android.content.Context;
36 | import android.util.AttributeSet;
37 | import android.view.LayoutInflater;
38 | import android.view.View;
39 | import android.widget.HeaderViewListAdapter;
40 | import android.widget.ListAdapter;
41 | import android.widget.ListView;
42 |
43 | import com.uit.pullrefresh.R;
44 |
45 | /**
46 | * @author mrsimple
47 | */
48 | public class RefreshLvLayout extends RefreshLayout {
49 |
50 | /**
51 | * ListView的加载中footer
52 | */
53 | protected View mListViewFooter;
54 |
55 | public RefreshLvLayout(Context context, AttributeSet attrs) {
56 | super(context, attrs);
57 | }
58 |
59 | public RefreshLvLayout(Context context) {
60 | this(context, null);
61 | mListViewFooter = LayoutInflater.from(context).inflate(R.layout.pull_to_refresh_footer,
62 | this, false);
63 | }
64 |
65 | /*
66 | * 设置ListView的adapter之前先添加footer,
67 | * 避免footer的添加在设置adapter之后出现的转换到HeaderViewListAdapter的异常
68 | * @see
69 | * com.uit.pullrefresh.swipe.RefreshLayout#setAdapter(android.widget.ListAdapter
70 | * )
71 | */
72 | @Override
73 | public void setAdapter(ListAdapter adapter) {
74 | if (mAbsListView.getFooterViewsCount() == 0) {
75 | mAbsListView.addFooterView(mListViewFooter);
76 | }
77 | super.setAdapter(adapter);
78 | // 添加只是为了在ListView的setAdapter方法时将Adapter包装成HeaderViewListAdapter。因此并不需要footer,因此添加后再移除,
79 | mAbsListView.removeFooterView(mListViewFooter);
80 | }
81 |
82 | @Override
83 | public void setLoading(boolean loading) {
84 | super.setLoading(loading);
85 | if (isLoading && mAbsListView.getFooterViewsCount() == 0) {
86 | mAbsListView.addFooterView(mListViewFooter);
87 | } else {
88 | if (mAbsListView.getAdapter() instanceof HeaderViewListAdapter) {
89 | mAbsListView.removeFooterView(mListViewFooter);
90 | } else {
91 | mListViewFooter.setVisibility(View.GONE);
92 | }
93 | mYDown = 0;
94 | mLastY = 0;
95 | }
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/android_my_pull_demo/res/layout/main.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
15 |
16 |
25 |
26 |
36 |
37 |
49 |
50 |
61 |
62 |
63 |
69 |
70 |
75 |
76 |
84 |
85 |
98 |
99 |
100 |
--------------------------------------------------------------------------------
/android_my_pull_demo/src/com/example/pull/MainActivity.java:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * created by Mr.Simple, Sep 12, 201411:18:06 AM.
4 | * Copyright (c) 2014, hehonghui@umeng.com All Rights Reserved.
5 | *
6 | * #####################################################
7 | * # #
8 | * # _oo0oo_ #
9 | * # o8888888o #
10 | * # 88" . "88 #
11 | * # (| -_- |) #
12 | * # 0\ = /0 #
13 | * # ___/`---'\___ #
14 | * # .' \\| |# '. #
15 | * # / \\||| : |||# \ #
16 | * # / _||||| -:- |||||- \ #
17 | * # | | \\\ - #/ | | #
18 | * # | \_| ''\---/'' |_/ | #
19 | * # \ .-\__ '-' ___/-. / #
20 | * # ___'. .' /--.--\ `. .'___ #
21 | * # ."" '< `.___\_<|>_/___.' >' "". #
22 | * # | | : `- \`.;`\ _ /`;.`/ - ` : | | #
23 | * # \ \ `_. \_ __\ /__ _/ .-` / / #
24 | * # =====`-.____`.___ \_____/___.-`___.-'===== #
25 | * # `=---=' #
26 | * # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #
27 | * # #
28 | * # 佛祖保佑 永无BUG #
29 | * # #
30 | * #####################################################
31 | */
32 |
33 | package com.example.pull;
34 |
35 | import android.app.Activity;
36 | import android.content.Intent;
37 | import android.os.Bundle;
38 | import android.view.View;
39 | import android.view.View.OnClickListener;
40 | import android.widget.ListView;
41 |
42 | import com.uit.pullrefresh.base.impl.PullRefreshListView;
43 |
44 | /**
45 | * @author mrsimple
46 | */
47 | public class MainActivity extends Activity {
48 |
49 | ListView mListView;
50 |
51 | PullRefreshListView mPullRefreshListView;
52 |
53 | @Override
54 | protected void onCreate(Bundle savedInstanceState) {
55 | super.onCreate(savedInstanceState);
56 | setContentView(R.layout.pull_refresh_main);
57 |
58 | findViewById(R.id.refresh_listview).setOnClickListener(new OnClickListener() {
59 |
60 | @Override
61 | public void onClick(View v) {
62 | Intent intent = new Intent(MainActivity.this, ShowActivity.class);
63 | intent.putExtra("index", ShowActivity.REFRESH_LV);
64 | startActivity(intent);
65 | }
66 | });
67 |
68 | findViewById(R.id.refresh_gridview).setOnClickListener(new OnClickListener() {
69 |
70 | @Override
71 | public void onClick(View v) {
72 | Intent intent = new Intent(MainActivity.this, ShowActivity.class);
73 | intent.putExtra("index", ShowActivity.REFRESH_GV);
74 | startActivity(intent);
75 | }
76 | });
77 |
78 | findViewById(R.id.refresh_textview).setOnClickListener(new OnClickListener() {
79 |
80 | @Override
81 | public void onClick(View v) {
82 | Intent intent = new Intent(MainActivity.this, ShowActivity.class);
83 | intent.putExtra("index", ShowActivity.REFRESH_TV);
84 | startActivity(intent);
85 | }
86 | });
87 |
88 | findViewById(R.id.refresh_slide_lv).setOnClickListener(new OnClickListener() {
89 |
90 | @Override
91 | public void onClick(View v) {
92 | Intent intent = new Intent(MainActivity.this, ShowActivity.class);
93 | intent.putExtra("index", ShowActivity.REFRESH_SLIDE_LV);
94 | startActivity(intent);
95 | }
96 | });
97 |
98 | // swipe layout
99 | findViewById(R.id.swipe_refresh_lv).setOnClickListener(new OnClickListener() {
100 |
101 | @Override
102 | public void onClick(View v) {
103 | Intent intent = new Intent(MainActivity.this, ShowActivity.class);
104 | intent.putExtra("index", ShowActivity.SWIPE_LV);
105 | startActivity(intent);
106 | }
107 | });
108 |
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | android_my_pull_refresh_view
2 | ============================
3 | # 概述
4 | > 这是一个通用的下拉刷新、上拉自动加载的组件,该组件继承自LinearLayout,方向为竖直布局,由三部分组成,分别是Header、ContentView、Foooter,其中ContentView的宽高都为match_parent,footer和header的宽、高分别为match_parent、wrap_content,在Header、Foooter初始时都会通过设置padding隐藏掉,只有
5 | ContentView区域显示出来。当用户下拉到顶端,并且继续下拉时触发下拉刷新操作;当用户上拉到底部,
6 | > 并且继续上拉时触发加载更多的操作。
7 | 更多内容请参考我的博客, CSDN博客
8 |
9 | **该项目中的库都是用于阐述基本原理,不建议使用到项目中。**
10 |
11 | ##一、布局示意图
12 | **原始布局**
13 | 
14 |
15 | **设置padding后headerh和footer偏移出屏幕**
16 | 
17 |
18 |
19 | ## 二、已有组件的使用示例
20 | 下面的例子都是在一个Activity中演示。
21 | ### 2.1 使用PullRefreshListView
22 | ```java
23 | public class MainActivity extends Activity {
24 |
25 | ListView mListView;
26 |
27 | PullRefreshListView mPullRefreshListView;
28 |
29 | @Override
30 | protected void onCreate(Bundle savedInstanceState) {
31 | super.onCreate(savedInstanceState);
32 |
33 | // 下拉刷新的listview
34 | mPullRefreshListView = new PullRefreshListView(this);
35 | // 获取listview 对象, 即PullRefreshListView中的mContentView
36 | mListView = mPullRefreshListView.getContentView();
37 |
38 | List datas = new ArrayList();
39 | for (int i = 0; i < 5; i++) {
40 | datas.add(" Item - " + i);
41 | }
42 |
43 | // 设置adapter
44 | mListView.setAdapter(new ArrayAdapter(this, android.R.layout.simple_list_item_1,
45 | datas));
46 |
47 | // 下拉刷新
48 | mPullRefreshListView.setOnRefreshListener(new OnPullRefreshListener() {
49 |
50 | @Override
51 | public void onRefresh() {
52 |
53 | Toast.makeText(getApplicationContext(), "refresh", Toast.LENGTH_SHORT).show();
54 | mPullRefreshListView.postDelayed(new Runnable() {
55 |
56 | @Override
57 | public void run() {
58 | mPullRefreshListView.refreshComplete();
59 | }
60 | }, 2000);
61 | }
62 | });
63 | // 上拉自动加载
64 | mPullRefreshListView.setOnLoadMoreListener(new OnLoadMoreListener() {
65 |
66 | @Override
67 | public void onLoadMore() {
68 | Toast.makeText(getApplicationContext(), "load more", Toast.LENGTH_SHORT).show();
69 | mPullRefreshListView.postDelayed(new Runnable() {
70 |
71 | @Override
72 | public void run() {
73 | mPullRefreshListView.loadMoreComplete();
74 | }
75 | }, 1500);
76 | }
77 | });
78 |
79 |
80 | setContentView(mPullRefreshListView);
81 |
82 | }
83 | }
84 | ```
85 | **下拉刷新截图**
86 |
87 | 
88 |
89 | **上拉到底部的自动加载**
90 | 
91 |
92 | ### 2.2 使用可下拉刷新的TextView
93 | ```java
94 |
95 | public class MainActivity extends Activity {
96 |
97 | @Override
98 | protected void onCreate(Bundle savedInstanceState) {
99 | super.onCreate(savedInstanceState);
100 |
101 | // PullRefreshTextView
102 | final PullRefreshTextView pullRefreshTextView = new PullRefreshTextView(this);
103 | pullRefreshTextView.getContentView().setText("下拉刷新TextView");
104 | // 下拉刷新
105 | pullRefreshTextView.setOnRefreshListener(new OnPullRefreshListener() {
106 |
107 | @Override
108 | public void onRefresh() {
109 | pullRefreshTextView.getContentView().setText(new Date().toGMTString());
110 | pullRefreshTextView.postDelayed(new Runnable() {
111 |
112 | @Override
113 | public void run() {
114 | pullRefreshTextView.refreshComplete();
115 | }
116 | }, 1000);
117 | }
118 | });
119 |
120 | // 上拉自动加载, TextView不能设置scroll listener ,所以无效
121 | pullRefreshTextView.setOnLoadMoreListener(new OnLoadMoreListener() {
122 |
123 | @Override
124 | public void onLoadMore() {
125 | Toast.makeText(getApplicationContext(), "textview load", Toast.LENGTH_SHORT).show();
126 | pullRefreshTextView.postDelayed(new Runnable() {
127 |
128 | @Override
129 | public void run() {
130 | pullRefreshTextView.loadMoreComplete();
131 | }
132 | }, 1000);
133 | }
134 | });
135 |
136 | setContentView(pullRefreshTextView);
137 |
138 |
139 | }
140 | }
141 | ```
142 | **截图**
143 |
144 | 
145 |
146 | ## 三、扩展该组件
147 | ### 3.1、继承自PullRefreshBase
148 | T为你要实现下拉刷新的View的类型,如ListView.
149 |
150 | ### 3.2、初始化ContentView
151 | 覆写initContentView方法,并且在该函数中初始化mContentView对象。我们以ListView为例,例如 :
152 | ```java
153 | /*
154 | * 初始化mContentView
155 | * @see com.uit.pullrefresh.base.PullRefreshBase#initContentView()
156 | */
157 | @Override
158 | protected void initContentView() {
159 | // 初始化mContentView
160 | mContentView = new ListView(getContext());
161 | // 设置OnScrollListener, 用以实现滑动到底部时的自动加载功能,如果不需要该功能可以不设置.
162 | mContentView.setOnScrollListener(this);
163 | }
164 | ```
165 |
166 | ### 3.3、覆写判断是否滑动到顶端和底部的方法
167 | 我们以ListView为例,例如 :
168 | ```java
169 | /*
170 | * 是否滑动到了顶端,如果返回true, 则表示到了顶端,用户继续下拉则触发下拉刷新
171 | * @see com.uit.pullrefresh.base.PullRefreshBase#isTop()
172 | */
173 | @Override
174 | protected boolean isTop() {
175 | View firstChild = mContentView.getChildAt(0);
176 | if (firstChild == null) {
177 | return true;
178 | }
179 | return mContentView.getFirstVisiblePosition() == 0
180 | && (firstChild.getTop() >= mContentView.getTop());
181 | }
182 |
183 | /*
184 | * 下拉到listview 最后一项时则返回true, 将出发自动加载
185 | * @see com.uit.pullrefresh.base.PullRefreshBase#isShowFooterView()
186 | */
187 | @Override
188 | protected boolean isShowFooterView() {
189 | if (mContentView == null || mContentView.getAdapter() == null) {
190 | return false;
191 | }
192 |
193 | return mContentView.getLastVisiblePosition() == mContentView.getAdapter().getCount() - 1;
194 | }
195 | ```
196 | ### 3.4使用即可
197 |
198 |
199 |
--------------------------------------------------------------------------------
/src/com/uit/pullrefresh/swipe/RefreshLayout.java:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * created by Mr.Simple, Oct 9, 20142:54:51 PM.
4 | * Copyright (c) 2014, hehonghui@umeng.com All Rights Reserved.
5 | *
6 | * #####################################################
7 | * # #
8 | * # _oo0oo_ #
9 | * # o8888888o #
10 | * # 88" . "88 #
11 | * # (| -_- |) #
12 | * # 0\ = /0 #
13 | * # ___/`---'\___ #
14 | * # .' \\| |# '. #
15 | * # / \\||| : |||# \ #
16 | * # / _||||| -:- |||||- \ #
17 | * # | | \\\ - #/ | | #
18 | * # | \_| ''\---/'' |_/ | #
19 | * # \ .-\__ '-' ___/-. / #
20 | * # ___'. .' /--.--\ `. .'___ #
21 | * # ."" '< `.___\_<|>_/___.' >' "". #
22 | * # | | : `- \`.;`\ _ /`;.`/ - ` : | | #
23 | * # \ \ `_. \_ __\ /__ _/ .-` / / #
24 | * # =====`-.____`.___ \_____/___.-`___.-'===== #
25 | * # `=---=' #
26 | * # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #
27 | * # #
28 | * # 佛祖保佑 永无BUG #
29 | * # #
30 | * #####################################################
31 | */
32 |
33 | package com.uit.pullrefresh.swipe;
34 |
35 | import android.content.Context;
36 | import android.support.v4.widget.SwipeRefreshLayout;
37 | import android.util.AttributeSet;
38 | import android.util.Log;
39 | import android.view.MotionEvent;
40 | import android.view.View;
41 | import android.view.ViewConfiguration;
42 | import android.widget.AbsListView;
43 | import android.widget.AbsListView.OnScrollListener;
44 | import android.widget.ListAdapter;
45 |
46 | import com.uit.pullrefresh.R;
47 |
48 | /**
49 | * 继承自SwipeRefreshLayout,从而实现滑动到底部时上拉加载更多的功能. 注意 :
50 | * 在下拉刷新完成时需要调用RefreshLayout的setRefreshing(false)方法来停止刷新过程;
51 | * 在上拉加载更多完成时需要调用setLoading(false)来标识加载完成。
52 | *
53 | * @author mrsimple
54 | */
55 | public abstract class RefreshLayout extends SwipeRefreshLayout implements
56 | OnScrollListener {
57 |
58 | /**
59 | * 滑动到最下面时的上拉操作
60 | */
61 |
62 | private int mTouchSlop;
63 | /**
64 | * listview实例
65 | */
66 | // private ListView mListView;
67 |
68 | /**
69 | *
70 | */
71 | protected T mAbsListView;
72 |
73 | /**
74 | * ListView滚动监听器,用于外部
75 | */
76 | private OnScrollListener mListViewOnScrollListener;
77 |
78 | /**
79 | * 上拉监听器, 到了最底部的上拉加载操作
80 | */
81 | private OnLoadListener mOnLoadListener;
82 |
83 | /**
84 | * 按下时的y坐标
85 | */
86 | protected int mYDown;
87 | /**
88 | * 抬起时的y坐标, 与mYDown一起用于滑动到底部时判断是上拉还是下拉
89 | */
90 | protected int mLastY;
91 | /**
92 | * 是否在加载中 ( 上拉加载更多 )
93 | */
94 | protected boolean isLoading = false;
95 |
96 | /**
97 | * @param context
98 | */
99 | public RefreshLayout(Context context) {
100 | this(context, null);
101 | }
102 |
103 | public RefreshLayout(Context context, AttributeSet attrs) {
104 | super(context, attrs);
105 |
106 | mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
107 | }
108 |
109 | @Override
110 | protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
111 | super.onLayout(changed, left, top, right, bottom);
112 |
113 | // 初始化ListView对象
114 | if (mAbsListView == null) {
115 | getRefreshView();
116 | // 设置颜色
117 | this.setColorScheme(R.color.umeng_comm_lv_header_color1,
118 | R.color.umeng_comm_lv_header_color2, R.color.umeng_comm_lv_header_color3,
119 | R.color.umeng_comm_lv_header_color4);
120 | }
121 | }
122 |
123 | /**
124 | * 获取ListView对象
125 | */
126 | @SuppressWarnings("unchecked")
127 | protected void getRefreshView() {
128 |
129 | Log.d(VIEW_LOG_TAG, "### 构造调用");
130 | int childs = getChildCount();
131 | if (childs > 0) {
132 | View childView = getChildAt(0);
133 | // if (childView instanceof ListView) {
134 | // mListView = (ListView) childView;
135 | // // 设置滚动监听器给ListView, 使得滚动的情况下也可以自动加载
136 | // mListView.setOnScrollListener(this);
137 | // Log.d(VIEW_LOG_TAG, "### 找到listview");
138 | // }
139 |
140 | if (childView instanceof AbsListView) {
141 | mAbsListView = (T) childView;
142 | // 设置滚动监听器给ListView, 使得滚动的情况下也可以自动加载
143 | mAbsListView.setOnScrollListener(this);
144 | Log.d(VIEW_LOG_TAG, "### 找到listview = " + mAbsListView);
145 | }
146 | }
147 | }
148 |
149 | /*
150 | * (non-Javadoc)
151 | * @see android.view.ViewGroup#dispatchTouchEvent(android.view.MotionEvent)
152 | */
153 | @Override
154 | public boolean dispatchTouchEvent(MotionEvent event) {
155 | final int action = event.getAction();
156 |
157 | switch (action) {
158 | case MotionEvent.ACTION_DOWN:
159 | // 按下
160 | mYDown = (int) event.getRawY();
161 | break;
162 |
163 | case MotionEvent.ACTION_MOVE:
164 | // 移动
165 | mLastY = (int) event.getRawY();
166 | break;
167 |
168 | case MotionEvent.ACTION_UP:
169 | // 抬起
170 | if (canLoad()) {
171 | loadData();
172 | }
173 | break;
174 | default:
175 | break;
176 | }
177 |
178 | return super.dispatchTouchEvent(event);
179 | }
180 |
181 | public void setAdapter(ListAdapter adapter) {
182 | mAbsListView.setAdapter(adapter);
183 | }
184 |
185 | /**
186 | * 是否可以加载更多, 条件是到了最底部, listview不在加载中, 且为上拉操作.
187 | *
188 | * @return
189 | */
190 | private boolean canLoad() {
191 | return isBottom() && !isLoading && isPullUp();
192 | }
193 |
194 | /**
195 | * 判断是否到了最底部
196 | */
197 | private boolean isBottom() {
198 | if (mAbsListView != null && mAbsListView.getAdapter() != null) {
199 | return mAbsListView.getLastVisiblePosition() == (mAbsListView.getAdapter().getCount() - 1);
200 | }
201 | return false;
202 | }
203 |
204 | /**
205 | * 是否是上拉操作
206 | *
207 | * @return
208 | */
209 | private boolean isPullUp() {
210 | return (mYDown - mLastY) >= mTouchSlop;
211 | }
212 |
213 | /**
214 | * 如果到了最底部,而且是上拉操作.那么执行onLoad方法
215 | */
216 | private void loadData() {
217 | if (mOnLoadListener != null) {
218 | // 设置状态
219 | setLoading(true);
220 | //
221 | mOnLoadListener.onLoad();
222 | }
223 | }
224 |
225 | /**
226 | * @param loading
227 | */
228 | public void setLoading(boolean loading) {
229 | isLoading = loading;
230 | }
231 |
232 | /**
233 | * 使外部可以监听到listview的滚动
234 | *
235 | * @param listener
236 | */
237 | public void addOnScrollListener(OnScrollListener listener) {
238 | mListViewOnScrollListener = listener;
239 | }
240 |
241 | /**
242 | * @param loadListener
243 | */
244 | public void setOnLoadListener(OnLoadListener loadListener) {
245 | mOnLoadListener = loadListener;
246 | }
247 |
248 | @Override
249 | public void onScrollStateChanged(AbsListView view, int scrollState) {
250 | Log.d(VIEW_LOG_TAG, "@@@@ state = " + scrollState);
251 |
252 | // 回调给外部的监听器
253 | if (mListViewOnScrollListener != null) {
254 | mListViewOnScrollListener.onScrollStateChanged(view, scrollState);
255 | }
256 | }
257 |
258 | @Override
259 | public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
260 | int totalItemCount) {
261 |
262 | // 回调给外部的监听器
263 | if (mListViewOnScrollListener != null) {
264 | mListViewOnScrollListener.onScroll(view, firstVisibleItem, visibleItemCount,
265 | totalItemCount);
266 | }
267 |
268 | // 滚动时到了最底部也可以加载更多
269 | if (canLoad()) {
270 | loadData();
271 | }
272 | }
273 |
274 | /**
275 | * 加载更多的监听器
276 | *
277 | * @author mrsimple
278 | */
279 | public static interface OnLoadListener {
280 | public void onLoad();
281 | }
282 | }
283 |
--------------------------------------------------------------------------------
/src/com/uit/pullrefresh/scroller/impl/RefreshSlideDeleteListView.java:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * created by Mr.Simple, Oct 2, 201410:21:12 AM.
4 | * Copyright (c) 2014, hehonghui@umeng.com All Rights Reserved.
5 | *
6 | * #####################################################
7 | * # #
8 | * # _oo0oo_ #
9 | * # o8888888o #
10 | * # 88" . "88 #
11 | * # (| -_- |) #
12 | * # 0\ = /0 #
13 | * # ___/`---'\___ #
14 | * # .' \\| |# '. #
15 | * # / \\||| : |||# \ #
16 | * # / _||||| -:- |||||- \ #
17 | * # | | \\\ - #/ | | #
18 | * # | \_| ''\---/'' |_/ | #
19 | * # \ .-\__ '-' ___/-. / #
20 | * # ___'. .' /--.--\ `. .'___ #
21 | * # ."" '< `.___\_<|>_/___.' >' "". #
22 | * # | | : `- \`.;`\ _ /`;.`/ - ` : | | #
23 | * # \ \ `_. \_ __\ /__ _/ .-` / / #
24 | * # =====`-.____`.___ \_____/___.-`___.-'===== #
25 | * # `=---=' #
26 | * # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #
27 | * # #
28 | * # 佛祖保佑 永无BUG #
29 | * # #
30 | * #####################################################
31 | */
32 |
33 | package com.uit.pullrefresh.scroller.impl;
34 |
35 | import android.content.Context;
36 | import android.support.v4.view.MotionEventCompat;
37 | import android.util.AttributeSet;
38 | import android.util.Log;
39 | import android.view.MotionEvent;
40 | import android.view.View;
41 | import android.widget.AbsListView;
42 |
43 | public class RefreshSlideDeleteListView extends RefreshListView {
44 |
45 | /**
46 | *
47 | */
48 | private int mXDown;
49 | /**
50 | *
51 | */
52 | private int mYDown;
53 | /**
54 | *
55 | */
56 | private View mItemView;
57 | /**
58 | * 删除区域的宽度, 这里假设宽度为200px吧
59 | */
60 | private int mDeleteViewWidth = 200;
61 |
62 | /**
63 | * 是否在滑动中
64 | */
65 | private boolean isSliding = false;
66 |
67 | /**
68 | * 是否已经显示
69 | */
70 | private boolean isDeleteViewShowing = false;
71 | /**
72 | *
73 | */
74 | int mItemPosition = 0;
75 |
76 | /**
77 | * @param context
78 | */
79 | public RefreshSlideDeleteListView(Context context) {
80 | this(context, null, 0);
81 | }
82 |
83 | public RefreshSlideDeleteListView(Context context, AttributeSet attrs) {
84 | this(context, attrs, 0);
85 | }
86 |
87 | public RefreshSlideDeleteListView(Context context, AttributeSet attrs, int defStyle) {
88 | super(context, attrs, defStyle);
89 | }
90 |
91 | /*
92 | * 在适当的时候拦截触摸事件,这里指的适当的时候是当mContentView滑动到顶部,并且是下拉时拦截触摸事件,否则不拦截,交给其child
93 | * view 来处理。
94 | * @see
95 | * android.view.ViewGroup#onInterceptTouchEvent(android.view.MotionEvent)
96 | */
97 | @Override
98 | public boolean onInterceptTouchEvent(MotionEvent ev) {
99 |
100 | /*
101 | * This method JUST determines whether we want to intercept the motion.
102 | * If we return true, onTouchEvent will be called and we do the actual
103 | * scrolling there.
104 | */
105 | final int action = MotionEventCompat.getActionMasked(ev);
106 | // Always handle the case of the touch gesture being complete.
107 | if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
108 | // Do not intercept touch event, let the child handle it
109 | return false;
110 | }
111 |
112 | Log.d(VIEW_LOG_TAG, "### is delete showing = " + isDeleteViewShowing);
113 |
114 | switch (action) {
115 |
116 | case MotionEvent.ACTION_DOWN:
117 | mLastY = (int) ev.getRawY();
118 | mXDown = (int) ev.getRawX();
119 | mYDown = mLastY;
120 | // 判断是否有删除按钮显示,如果有则隐藏删除按钮
121 | if (isDeleteViewShowing && mItemPosition != AbsListView.INVALID_POSITION
122 | && mItemPosition != getItemPosition(mXDown, mYDown)) {
123 | slideItemView(0);
124 | clearSlideState();
125 | return false;
126 | }
127 | break;
128 |
129 | case MotionEvent.ACTION_MOVE:
130 | // int yDistance = (int) ev.getRawY() - mYDown;
131 | mYOffset = (int) ev.getRawY() - mLastY;
132 | mDistanceX = (int) ev.getRawX() - mXDown;
133 | // 如果拉到了顶部, 并且是下拉,则拦截触摸事件,从而转到onTouchEvent来处理下拉刷新事件
134 | if (isTop() && mYOffset > 0) {
135 | return true;
136 | }
137 | // 滑动删除操作
138 | if (Math.abs(mDistanceX) > 10) {
139 | return true;
140 | }
141 | break;
142 |
143 | }
144 |
145 | // Do not intercept touch event, let the child handle it
146 | return false;
147 | }
148 |
149 | /*
150 | * 在这里处理触摸事件以达到下拉刷新或者上拉自动加载的问题
151 | * @see android.view.View#onTouchEvent(android.view.MotionEvent)
152 | */
153 | @Override
154 | public boolean onTouchEvent(MotionEvent event) {
155 |
156 | switch (event.getAction()) {
157 | case MotionEvent.ACTION_DOWN:
158 | mLastY = (int) event.getRawY();
159 | break;
160 |
161 | case MotionEvent.ACTION_MOVE:
162 | int currentY = (int) event.getRawY();
163 | int currentX = (int) event.getRawX();
164 | mDistanceX = currentX - mXDown;
165 | int distanceY = (int) event.getRawY() - mYDown;
166 | Log.d(VIEW_LOG_TAG, "### distanceX = " + mDistanceX + ", dis y = " + distanceY
167 | + ", is delete showing = " + isDeleteViewShowing);
168 | if (Math.abs(mDistanceX) > Math.abs(distanceY) * 2) {
169 | if (mItemView == null) {
170 | mItemView = getCurrentItemView(currentX, currentY);
171 | }
172 | slideItemView(mDistanceX);
173 | } else if (!isDeleteViewShowing && isTop() && mYOffset > 0 && !isSliding) {
174 | //
175 | mYOffset = currentY - mLastY;
176 | if (mCurrentStatus != STATUS_LOADING) {
177 | // 修改ScrollY达到拉伸header的效果
178 | changeScrollY(mYOffset);
179 | }
180 |
181 | rotateHeaderArrow();
182 | changeTips();
183 | mLastY = currentY;
184 | }
185 | break;
186 |
187 | case MotionEvent.ACTION_UP:
188 | isSlideValid();
189 | // 下拉刷新的具体操作
190 | doRefresh();
191 | break;
192 | default:
193 | break;
194 |
195 | }
196 |
197 | return true;
198 | }
199 |
200 | int mDistanceX = 0;
201 |
202 | /**
203 | *
204 | */
205 | private void slideItemView(int distanceX) {
206 | if (mCurrentStatus == STATUS_PULL_TO_REFRESH
207 | || mCurrentStatus == STATUS_RELEASE_TO_REFRESH) {
208 | return;
209 | }
210 |
211 | // 在没有delete view显示,且是用户右滑则忽略
212 | if (distanceX > 0 && !isDeleteViewShowing) {
213 | return;
214 | }
215 |
216 | Log.d(VIEW_LOG_TAG, "### slide item view , origin distance x = " + distanceX);
217 | if (distanceX <= 0) {
218 | // 小于0,代表用户左滑,这里将其转为正数,使得整个item view的视图向右滑动,即在x轴上要向右移动。
219 | distanceX = Math.abs(distanceX);
220 | distanceX = Math.min(distanceX, mDeleteViewWidth);
221 | } else {
222 | distanceX = Math.max(0, mDeleteViewWidth - distanceX);
223 | }
224 |
225 | Log.d(VIEW_LOG_TAG, "### slide item view , distance x = " + distanceX);
226 | Log.d(VIEW_LOG_TAG, "item view = " + mItemView);
227 | if (mItemView != null) {
228 | mItemView.scrollTo(distanceX, 0);
229 | mItemView.invalidate();
230 | isSliding = true;
231 | }
232 |
233 | }
234 |
235 | /**
236 | *
237 | */
238 | private void clearSlideState() {
239 | mItemView = null;
240 | isDeleteViewShowing = false;
241 | isSliding = false;
242 | mDistanceX = 0;
243 | mItemPosition = AbsListView.INVALID_POSITION;
244 | }
245 |
246 | /**
247 | *
248 | */
249 | private void isSlideValid() {
250 | if (mItemView != null) {
251 | if (mItemView.getScrollX() > mDeleteViewWidth / 2) {
252 | slideItemView(-mDeleteViewWidth);
253 | isDeleteViewShowing = true;
254 | } else {
255 | slideItemView(0);
256 | clearSlideState();
257 | }
258 | } else {
259 | clearSlideState();
260 | }
261 |
262 | }
263 |
264 | /**
265 | * @param position
266 | */
267 | public void onRemoveItem(int position) {
268 | slideItemView(0);
269 | clearSlideState();
270 | }
271 |
272 | /**
273 | * @param x
274 | * @param y
275 | * @return
276 | */
277 | private int getItemPosition(int x, int y) {
278 | int position = mContentView.pointToPosition(x, y);
279 | if (position != AbsListView.INVALID_POSITION) {
280 |
281 | Log.d(VIEW_LOG_TAG,
282 | "### first = " + mContentView.getFirstVisiblePosition() + ", touch postion = "
283 | + position + ", header count = " + mContentView.getHeaderViewsCount());
284 |
285 | mItemPosition = position - mContentView.getFirstVisiblePosition() - 1;
286 | mItemPosition = Math.max(0, mItemPosition);
287 | }
288 |
289 | return mItemPosition;
290 | }
291 |
292 | /**
293 | * @param event
294 | * @return
295 | */
296 | private View getCurrentItemView(int x, int y) {
297 | //
298 | getItemPosition(x, y);
299 |
300 | Log.d(VIEW_LOG_TAG, "### child count = " + mContentView.getChildCount());
301 | //
302 | if (mItemPosition != AbsListView.INVALID_POSITION) {
303 | // get child view
304 | return mContentView.getChildAt(mItemPosition);
305 | }
306 |
307 | return null;
308 | }
309 |
310 | }
311 |
--------------------------------------------------------------------------------
/android_my_pull_demo/src/com/example/pull/ShowActivity.java:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * created by Mr.Simple, Sep 30, 201411:50:14 PM.
4 | * Copyright (c) 2014, hehonghui@umeng.com All Rights Reserved.
5 | *
6 | * #####################################################
7 | * # #
8 | * # _oo0oo_ #
9 | * # o8888888o #
10 | * # 88" . "88 #
11 | * # (| -_- |) #
12 | * # 0\ = /0 #
13 | * # ___/`---'\___ #
14 | * # .' \\| |# '. #
15 | * # / \\||| : |||# \ #
16 | * # / _||||| -:- |||||- \ #
17 | * # | | \\\ - #/ | | #
18 | * # | \_| ''\---/'' |_/ | #
19 | * # \ .-\__ '-' ___/-. / #
20 | * # ___'. .' /--.--\ `. .'___ #
21 | * # ."" '< `.___\_<|>_/___.' >' "". #
22 | * # | | : `- \`.;`\ _ /`;.`/ - ` : | | #
23 | * # \ \ `_. \_ __\ /__ _/ .-` / / #
24 | * # =====`-.____`.___ \_____/___.-`___.-'===== #
25 | * # `=---=' #
26 | * # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #
27 | * # #
28 | * # 佛祖保佑 永无BUG #
29 | * # #
30 | * #####################################################
31 | */
32 |
33 | package com.example.pull;
34 |
35 | import android.app.Activity;
36 | import android.graphics.Color;
37 | import android.os.Bundle;
38 | import android.support.v4.widget.SwipeRefreshLayout;
39 | import android.view.Gravity;
40 | import android.view.View;
41 | import android.view.View.OnClickListener;
42 | import android.widget.ArrayAdapter;
43 | import android.widget.BaseAdapter;
44 | import android.widget.ListView;
45 | import android.widget.TextView;
46 | import android.widget.Toast;
47 |
48 | import com.uit.commons.CommonAdapter;
49 | import com.uit.commons.CommonViewHolder;
50 | import com.uit.pullrefresh.listener.OnLoadListener;
51 | import com.uit.pullrefresh.listener.OnRefreshListener;
52 | import com.uit.pullrefresh.scroller.impl.RefreshGridView;
53 | import com.uit.pullrefresh.scroller.impl.RefreshListView;
54 | import com.uit.pullrefresh.scroller.impl.RefreshSlideDeleteListView;
55 | import com.uit.pullrefresh.scroller.impl.RefreshTextView;
56 | import com.uit.pullrefresh.swipe.RefreshLayout;
57 | import com.uit.pullrefresh.swipe.RefreshLvLayout;
58 |
59 | import java.text.SimpleDateFormat;
60 | import java.util.ArrayList;
61 | import java.util.Date;
62 | import java.util.List;
63 |
64 | /**
65 | * @author mrsimple
66 | */
67 | public class ShowActivity extends Activity {
68 |
69 | public static final int REFRESH_LV = 1;
70 | public static final int REFRESH_GV = 2;
71 | public static final int REFRESH_TV = 3;
72 | public static final int REFRESH_SLIDE_LV = 4;
73 | public static final int SWIPE_LV = 5;
74 |
75 | //
76 | final List dataStrings = new ArrayList();
77 |
78 | @Override
79 | protected void onCreate(Bundle savedInstanceState) {
80 | super.onCreate(savedInstanceState);
81 |
82 | int index = 0;
83 | Bundle extraBundle = getIntent().getExtras();
84 | if (extraBundle != null && extraBundle.containsKey("index")) {
85 | index = extraBundle.getInt("index");
86 | }
87 |
88 | // 准备数据
89 | for (int i = 0; i < 20; i++) {
90 | dataStrings.add("item - " + i);
91 | }
92 |
93 | switch (index) {
94 | case REFRESH_LV:
95 | setListView();
96 | break;
97 | case REFRESH_GV:
98 | setGridView();
99 | break;
100 | case REFRESH_TV:
101 | setTextView();
102 | break;
103 | case REFRESH_SLIDE_LV:
104 | setSlideListView();
105 | break;
106 | case SWIPE_LV:
107 | setSwipeRefreshListView();
108 | break;
109 | default:
110 | break;
111 | }
112 | }
113 |
114 | /**
115 | *
116 | */
117 | private void setListView() {
118 | final RefreshListView refreshLayout = new RefreshListView(this);
119 |
120 | // 获取ListView, 这里的listview就是Content view
121 | refreshLayout.setAdapter(new ArrayAdapter(this,
122 | android.R.layout.simple_list_item_1, dataStrings));
123 | // 设置下拉刷新监听器
124 | refreshLayout.setOnRefreshListener(new OnRefreshListener() {
125 |
126 | @Override
127 | public void onRefresh() {
128 | Toast.makeText(getApplicationContext(), "refreshing", Toast.LENGTH_SHORT)
129 | .show();
130 |
131 | refreshLayout.postDelayed(new Runnable() {
132 |
133 | @Override
134 | public void run() {
135 | refreshLayout.refreshComplete();
136 | }
137 | }, 1500);
138 | }
139 | });
140 |
141 | // 不设置的话到底部不会自动加载
142 | refreshLayout.setOnLoadListener(new OnLoadListener() {
143 |
144 | @Override
145 | public void onLoadMore() {
146 | Toast.makeText(getApplicationContext(), "loading", Toast.LENGTH_SHORT)
147 | .show();
148 |
149 | refreshLayout.postDelayed(new Runnable() {
150 |
151 | @Override
152 | public void run() {
153 | refreshLayout.loadCompelte();
154 | }
155 | }, 1500);
156 | }
157 | });
158 |
159 | //
160 | setContentView(refreshLayout);
161 | }
162 |
163 | /**
164 | *
165 | */
166 | private void setGridView() {
167 | // gridview
168 | final RefreshGridView gv = new RefreshGridView(this);
169 | String[] dataStrings = new String[20];
170 | for (int i = 0; i < dataStrings.length; i++) {
171 | dataStrings[i] = "item - " +
172 | i;
173 | }
174 | gv.setAdapter(new ArrayAdapter(this,
175 | android.R.layout.simple_list_item_1, dataStrings));
176 | //
177 | gv.setOnRefreshListener(new OnRefreshListener() {
178 |
179 | @Override
180 | public void onRefresh() {
181 | Toast.makeText(getApplicationContext(), "refreshing", Toast.LENGTH_SHORT)
182 | .show();
183 |
184 | gv.postDelayed(new Runnable() {
185 |
186 | @Override
187 | public void run() {
188 | gv.refreshComplete();
189 | }
190 | }, 1500);
191 | }
192 | });
193 |
194 | // 不设置的话到底部不会自动加载
195 | gv.setOnLoadListener(new OnLoadListener() {
196 |
197 | @Override
198 | public void onLoadMore() {
199 | Toast.makeText(getApplicationContext(), "loading", Toast.LENGTH_SHORT)
200 | .show();
201 |
202 | gv.postDelayed(new Runnable() {
203 |
204 | @Override
205 | public void run() {
206 | gv.loadCompelte();
207 | }
208 | }, 1500);
209 | }
210 | });
211 |
212 | setContentView(gv);
213 |
214 | }
215 |
216 | /**
217 | *
218 | */
219 | private void setTextView() {
220 | final RefreshTextView refreshTextView = new RefreshTextView(this);
221 | //
222 | final TextView tv = refreshTextView.getContentView();
223 | tv.setText("下拉更新时间的TextView");
224 | tv.setGravity(Gravity.CENTER);
225 | tv.setTextSize(30f);
226 | tv.setBackgroundColor(Color.YELLOW);
227 | refreshTextView.setOnRefreshListener(new OnRefreshListener() {
228 |
229 | @Override
230 | public void onRefresh() {
231 | refreshTextView.postDelayed(new Runnable() {
232 |
233 | @Override
234 | public void run() {
235 | SimpleDateFormat sdf = (SimpleDateFormat) SimpleDateFormat.getInstance();
236 | sdf.applyPattern("yyyy-MM-dd hh:mm:ss");
237 | refreshTextView.getContentView().setText("下拉更新时间的TextView " +
238 | System.getProperty("line.separator") + sdf.format(new Date()));
239 | refreshTextView.refreshComplete();
240 | }
241 | }, 1500);
242 | }
243 | });
244 | //
245 | setContentView(refreshTextView);
246 | }
247 |
248 | // 适配器
249 | final BaseAdapter myAdapter = new CommonAdapter(this,
250 | R.layout.slide_item_layout,
251 | dataStrings) {
252 |
253 | @Override
254 | protected void fillItemData(CommonViewHolder viewHolder, final int
255 | position,
256 | String item) {
257 |
258 | viewHolder.setTextForTextView(R.id.text_tv, item);
259 | //
260 | viewHolder.setOnClickListener(R.id.delete, new OnClickListener() {
261 |
262 | @Override
263 | public void onClick(View v) {
264 | //
265 | slideDeleteListView.onRemoveItem(position);
266 | dataStrings.remove(position);
267 | myAdapter.notifyDataSetChanged();
268 | }
269 | });
270 | }
271 | };
272 |
273 | RefreshSlideDeleteListView slideDeleteListView;
274 |
275 | /**
276 | *
277 | */
278 | private void setSlideListView() {
279 |
280 | slideDeleteListView = new RefreshSlideDeleteListView(this);
281 |
282 | //
283 | slideDeleteListView.setAdapter(myAdapter);
284 | //
285 | slideDeleteListView.setOnRefreshListener(new OnRefreshListener() {
286 |
287 | @Override
288 | public void onRefresh() {
289 | slideDeleteListView.postDelayed(new Runnable() {
290 |
291 | @Override
292 | public void run() {
293 | slideDeleteListView.refreshComplete();
294 | }
295 | }, 1500);
296 | }
297 | });
298 | //
299 | setContentView(slideDeleteListView);
300 | }
301 |
302 | /**
303 | *
304 | */
305 | private void setSwipeRefreshListView() {
306 | //
307 | setContentView(R.layout.swipe_lv_layout);
308 |
309 | // 注意, 这是一个ViewGroup类, 不是listview本身
310 | final RefreshLvLayout refreshLayout = (RefreshLvLayout) findViewById(R.id.swipe_layout);
311 | // 注意这里的是SwipeRefreshLayout.OnRefreshListener
312 | refreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
313 |
314 | @Override
315 | public void onRefresh() {
316 |
317 | Toast.makeText(getApplicationContext(), "on refresh ", Toast.LENGTH_SHORT).show();
318 |
319 | // 如果没有时间消耗,那么加载效果很快消失了
320 | refreshLayout.postDelayed(new Runnable() {
321 |
322 | @Override
323 | public void run() {
324 | // 刷新完以后调用该方法
325 | refreshLayout.setRefreshing(false);
326 | }
327 | }, 1500);
328 | }
329 | });
330 |
331 | // 滑动到底部再上拉则加载横多
332 | refreshLayout.setOnLoadListener(new RefreshLayout.OnLoadListener() {
333 |
334 | @Override
335 | public void onLoad() {
336 | Toast.makeText(getApplicationContext(), "on load ( 加载1.5秒 )", Toast.LENGTH_SHORT)
337 | .show();
338 | // 如果没有时间消耗,那么加载效果很快消失了,相当于没有显示footer视图一样.
339 | refreshLayout.postDelayed(new Runnable() {
340 |
341 | @Override
342 | public void run() {
343 | // 加载完以后调用该方法
344 | refreshLayout.setLoading(false);
345 | }
346 | }, 1500);
347 | }
348 | });
349 |
350 | // listview
351 | final ListView listView = (ListView) findViewById(R.id.swipe_listview);
352 |
353 | // 设置适配器
354 | listView.setAdapter(new ArrayAdapter(this,
355 | android.R.layout.simple_list_item_1, dataStrings));
356 | }
357 | }
358 |
--------------------------------------------------------------------------------
/src/com/uit/pullrefresh/scroller/RefreshLayoutBase.java:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * created by Mr.Simple, Sep 30, 20142:48:17 PM.
4 | * Copyright (c) 2014, hehonghui@umeng.com All Rights Reserved.
5 | *
6 | * #####################################################
7 | * # #
8 | * # _oo0oo_ #
9 | * # o8888888o #
10 | * # 88" . "88 #
11 | * # (| -_- |) #
12 | * # 0\ = /0 #
13 | * # ___/`---'\___ #
14 | * # .' \\| |# '. #
15 | * # / \\||| : |||# \ #
16 | * # / _||||| -:- |||||- \ #
17 | * # | | \\\ - #/ | | #
18 | * # | \_| ''\---/'' |_/ | #
19 | * # \ .-\__ '-' ___/-. / #
20 | * # ___'. .' /--.--\ `. .'___ #
21 | * # ."" '< `.___\_<|>_/___.' >' "". #
22 | * # | | : `- \`.;`\ _ /`;.`/ - ` : | | #
23 | * # \ \ `_. \_ __\ /__ _/ .-` / / #
24 | * # =====`-.____`.___ \_____/___.-`___.-'===== #
25 | * # `=---=' #
26 | * # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #
27 | * # #
28 | * # 佛祖保佑 永无BUG #
29 | * # #
30 | * #####################################################
31 | */
32 |
33 | package com.uit.pullrefresh.scroller;
34 |
35 | import android.content.Context;
36 | import android.graphics.Color;
37 | import android.support.v4.view.MotionEventCompat;
38 | import android.util.AttributeSet;
39 | import android.util.Log;
40 | import android.view.LayoutInflater;
41 | import android.view.MotionEvent;
42 | import android.view.View;
43 | import android.view.ViewGroup;
44 | import android.view.animation.RotateAnimation;
45 | import android.widget.AbsListView;
46 | import android.widget.AbsListView.OnScrollListener;
47 | import android.widget.ImageView;
48 | import android.widget.ProgressBar;
49 | import android.widget.Scroller;
50 | import android.widget.TextView;
51 |
52 | import com.uit.pullrefresh.R;
53 | import com.uit.pullrefresh.listener.OnLoadListener;
54 | import com.uit.pullrefresh.listener.OnRefreshListener;
55 |
56 | import java.text.SimpleDateFormat;
57 | import java.util.Date;
58 |
59 | /**
60 | * RefreshLayoutBase是下拉刷新、上拉加载更多的抽象类。该组件含有Header、Content
61 | * View、Footer三部分,并且从上到下布局。Header、Footer一般大小固定,Content
62 | * View则为与屏幕一样大。在初始化时通过Scroller将Header滚出屏幕的可见区域
63 | * ,当下拉时将Header慢慢移入用户的视野,用户抬起手指时判断Header的可见高度
64 | * ,如果可见的高度大于Header总高度的一半,那么则触发下拉刷新操作。加载更多则是当Content
65 | * View到了底部,用户还继续上拉,那么触发加载更多操作。
66 | *
67 | * @author mrsimple
68 | * @param Content View的类型,可以为任何View的子类,例如ListView、GridView等
69 | */
70 | public abstract class RefreshLayoutBase extends ViewGroup implements
71 | OnScrollListener {
72 |
73 | /**
74 | *
75 | */
76 | protected Scroller mScroller;
77 |
78 | /**
79 | * 下拉刷新时显示的header view
80 | */
81 | protected View mHeaderView;
82 |
83 | /**
84 | * 上拉加载更多时显示的footer view
85 | */
86 | protected View mFooterView;
87 |
88 | /**
89 | * 本次触摸滑动y坐标上的偏移量
90 | */
91 | protected int mYOffset;
92 |
93 | /**
94 | * 内容视图, 即用户触摸导致下拉刷新、上拉加载的主视图. 比如ListView, GridView等.
95 | */
96 | protected T mContentView;
97 |
98 | /**
99 | * 最初的滚动位置.第一次布局时滚动header的高度的距离
100 | */
101 | protected int mInitScrollY = 0;
102 | /**
103 | * 最后一次触摸事件的y轴坐标
104 | */
105 | protected int mLastY = 0;
106 |
107 | /**
108 | * 空闲状态
109 | */
110 | public static final int STATUS_IDLE = 0;
111 |
112 | /**
113 | * 下拉或者上拉状态, 还没有到达可刷新的状态
114 | */
115 | public static final int STATUS_PULL_TO_REFRESH = 1;
116 |
117 | /**
118 | * 下拉或者上拉状态
119 | */
120 | public static final int STATUS_RELEASE_TO_REFRESH = 2;
121 | /**
122 | * 刷新中
123 | */
124 | public static final int STATUS_REFRESHING = 3;
125 |
126 | /**
127 | * LOADING中
128 | */
129 | public static final int STATUS_LOADING = 4;
130 |
131 | /**
132 | * 当前状态
133 | */
134 | protected int mCurrentStatus = STATUS_IDLE;
135 |
136 | /**
137 | * header中的箭头图标
138 | */
139 | private ImageView mArrowImageView;
140 | /**
141 | * 箭头是否向上
142 | */
143 | private boolean isArrowUp;
144 | /**
145 | * header 中的文本标签
146 | */
147 | private TextView mTipsTextView;
148 | /**
149 | * header中的时间标签
150 | */
151 | private TextView mTimeTextView;
152 | /**
153 | * header中的进度条
154 | */
155 | private ProgressBar mProgressBar;
156 | /**
157 | * 屏幕高度
158 | */
159 | private int mScreenHeight;
160 | /**
161 | * Header 高度
162 | */
163 | private int mHeaderHeight;
164 | /**
165 | * 下拉刷新监听器
166 | */
167 | protected OnRefreshListener mOnRefreshListener;
168 | /**
169 | * 加载更多回调
170 | */
171 | protected OnLoadListener mLoadListener;
172 |
173 | /**
174 | * @param context
175 | */
176 | public RefreshLayoutBase(Context context) {
177 | this(context, null);
178 | }
179 |
180 | /**
181 | * @param context
182 | * @param attrs
183 | */
184 | public RefreshLayoutBase(Context context, AttributeSet attrs) {
185 | this(context, attrs, 0);
186 | }
187 |
188 | /**
189 | * @param context
190 | * @param attrs
191 | * @param defStyle
192 | */
193 | public RefreshLayoutBase(Context context, AttributeSet attrs, int defStyle) {
194 | super(context, attrs);
195 |
196 | // 初始化Scroller对象
197 | mScroller = new Scroller(context);
198 |
199 | // 获取屏幕高度
200 | mScreenHeight = context.getResources().getDisplayMetrics().heightPixels;
201 | // header 的高度为屏幕高度的 1/4
202 | mHeaderHeight = mScreenHeight / 4;
203 |
204 | // 初始化整个布局
205 | initLayout(context);
206 | }
207 |
208 | /**
209 | * 初始化整个布局
210 | *
211 | * @param context
212 | */
213 | private final void initLayout(Context context) {
214 | // header view
215 | setupHeaderView(context);
216 | // 设置内容视图
217 | setupContentView(context);
218 | // 设置布局参数
219 | setDefaultContentLayoutParams();
220 | // 添加mContentView
221 | addView(mContentView);
222 | // footer view
223 | setupFooterView(context);
224 |
225 | }
226 |
227 | /**
228 | * 初始化 header view
229 | */
230 | protected void setupHeaderView(Context context) {
231 | mHeaderView = LayoutInflater.from(context).inflate(R.layout.pull_to_refresh_header, this,
232 | false);
233 | mHeaderView
234 | .setLayoutParams(new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT,
235 | mHeaderHeight));
236 | mHeaderView.setBackgroundColor(Color.RED);
237 | mHeaderView.setPadding(0, mHeaderHeight - 100, 0, 0);
238 | addView(mHeaderView);
239 |
240 | // HEADER VIEWS
241 | mArrowImageView = (ImageView) mHeaderView.findViewById(R.id.pull_to_arrow_image);
242 | mTipsTextView = (TextView) mHeaderView.findViewById(R.id.pull_to_refresh_text);
243 | mTimeTextView = (TextView) mHeaderView.findViewById(R.id.pull_to_refresh_updated_at);
244 | mProgressBar = (ProgressBar) mHeaderView.findViewById(R.id.pull_to_refresh_progress);
245 | }
246 |
247 | /*
248 | * 丈量视图的宽、高。宽度为用户设置的宽度,高度则为header, content view, footer这三个子控件的高度只和。
249 | * @see android.view.View#onMeasure(int, int)
250 | */
251 | @Override
252 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
253 | int width = MeasureSpec.getSize(widthMeasureSpec);
254 | int childCount = getChildCount();
255 | int finalHeight = 0;
256 | for (int i = 0; i < childCount; i++) {
257 | View child = getChildAt(i);
258 | // measure
259 | measureChild(child, widthMeasureSpec, heightMeasureSpec);
260 | // 该view所需要的总高度
261 | finalHeight += child.getMeasuredHeight();
262 | }
263 | setMeasuredDimension(width, finalHeight);
264 | }
265 |
266 | /*
267 | * 布局函数,将header, content view,
268 | * footer这三个view从上到下布局。布局完成后通过Scroller滚动到header的底部,即滚动距离为header的高度 +
269 | * 本视图的paddingTop,从而达到隐藏header的效果.
270 | * @see android.view.ViewGroup#onLayout(boolean, int, int, int, int)
271 | */
272 | @Override
273 | protected void onLayout(boolean changed, int l, int t, int r, int b) {
274 |
275 | int childCount = getChildCount();
276 | int top = getPaddingTop();
277 | for (int i = 0; i < childCount; i++) {
278 | View child = getChildAt(i);
279 | child.layout(0, top, child.getMeasuredWidth(), child.getMeasuredHeight() + top);
280 | top += child.getMeasuredHeight();
281 | }
282 |
283 | // 计算初始化滑动的y轴距离
284 | mInitScrollY = mHeaderView.getMeasuredHeight() + getPaddingTop();
285 | // 滑动到header view高度的位置, 从而达到隐藏header view的效果
286 | scrollTo(0, mInitScrollY);
287 | }
288 |
289 | /**
290 | * 初始化Content View, 子类覆写.
291 | */
292 | protected abstract void setupContentView(Context context);
293 |
294 | /**
295 | * 是否已经到了最顶部,子类需覆写该方法,使得mContentView滑动到最顶端时返回true, 如果到达最顶端用户继续下拉则拦截事件;
296 | *
297 | * @return
298 | */
299 | protected abstract boolean isTop();
300 |
301 | /**
302 | * 是否已经到了最底部,子类需覆写该方法,使得mContentView滑动到最底端时返回true;从而触发自动加载更多的操作
303 | *
304 | * @return
305 | */
306 | protected abstract boolean isBottom();
307 |
308 | /**
309 | * 初始化footer view
310 | */
311 | protected void setupFooterView(Context context) {
312 | mFooterView = LayoutInflater.from(context).inflate(R.layout.pull_to_refresh_footer,
313 | this, false);
314 | addView(mFooterView);
315 | }
316 |
317 | /**
318 | * 设置Content View的默认布局参数
319 | */
320 | protected void setDefaultContentLayoutParams() {
321 | ViewGroup.LayoutParams params =
322 | new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT,
323 | LayoutParams.MATCH_PARENT);
324 | mContentView.setLayoutParams(params);
325 | }
326 |
327 | /**
328 | * 与Scroller合作,实现平滑滚动。在该方法中调用Scroller的computeScrollOffset来判断滚动是否结束。如果没有结束,
329 | * 那么滚动到相应的位置,并且调用postInvalidate方法重绘界面,从而再次进入到这个computeScroll流程,直到滚动结束。
330 | */
331 | @Override
332 | public void computeScroll() {
333 | if (mScroller.computeScrollOffset()) {
334 | scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
335 | postInvalidate();
336 | }
337 | }
338 |
339 | /*
340 | * 在适当的时候拦截触摸事件,这里指的适当的时候是当mContentView滑动到顶部,并且是下拉时拦截触摸事件,否则不拦截,交给其child
341 | * view 来处理。
342 | * @see
343 | * android.view.ViewGroup#onInterceptTouchEvent(android.view.MotionEvent)
344 | */
345 | @Override
346 | public boolean onInterceptTouchEvent(MotionEvent ev) {
347 |
348 | /*
349 | * This method JUST determines whether we want to intercept the motion.
350 | * If we return true, onTouchEvent will be called and we do the actual
351 | * scrolling there.
352 | */
353 | final int action = MotionEventCompat.getActionMasked(ev);
354 | // Always handle the case of the touch gesture being complete.
355 | if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
356 | // Do not intercept touch event, let the child handle it
357 | return false;
358 | }
359 |
360 | switch (action) {
361 |
362 | case MotionEvent.ACTION_DOWN:
363 | mLastY = (int) ev.getRawY();
364 | break;
365 |
366 | case MotionEvent.ACTION_MOVE:
367 | // int yDistance = (int) ev.getRawY() - mYDown;
368 | mYOffset = (int) ev.getRawY() - mLastY;
369 | // 如果拉到了顶部, 并且是下拉,则拦截触摸事件,从而转到onTouchEvent来处理下拉刷新事件
370 | if (isTop() && mYOffset > 0) {
371 | return true;
372 | }
373 | break;
374 |
375 | }
376 | // Do not intercept touch event, let the child handle it
377 | return false;
378 | }
379 |
380 | /*
381 | * 在这里处理触摸事件以达到下拉刷新或者上拉自动加载的问题
382 | * @see android.view.View#onTouchEvent(android.view.MotionEvent)
383 | */
384 | @Override
385 | public boolean onTouchEvent(MotionEvent event) {
386 | Log.d(VIEW_LOG_TAG, "@@@ onTouchEvent : action = " + event.getAction());
387 | switch (event.getAction()) {
388 | case MotionEvent.ACTION_MOVE:
389 | int currentY = (int) event.getRawY();
390 | mYOffset = currentY - mLastY;
391 | if (mCurrentStatus != STATUS_LOADING) {
392 | changeScrollY(mYOffset);
393 | }
394 |
395 | rotateHeaderArrow();
396 | changeTips();
397 | mLastY = currentY;
398 | break;
399 |
400 | case MotionEvent.ACTION_UP:
401 | // 下拉刷新的具体操作
402 | doRefresh();
403 | break;
404 | default:
405 | break;
406 | }
407 | return true;
408 | }
409 |
410 | /**
411 | * 设置滚动的参数
412 | *
413 | * @param yOffset
414 | */
415 | private void startScroll(int yOffset) {
416 | mScroller.startScroll(getScrollX(), getScrollY(), 0, yOffset);
417 | invalidate();
418 | }
419 |
420 | /**
421 | * @param distance
422 | * @return
423 | */
424 | protected void changeScrollY(int distance) {
425 | // 最大值为 scrollY(header 隐藏), 最小值为0 ( header 完全显示).
426 | int curY = getScrollY();
427 | // 下拉
428 | if (distance > 0 && curY - distance > getPaddingTop()) {
429 | scrollBy(0, -distance);
430 | } else if (distance < 0 && curY - distance <= mInitScrollY) {
431 | // 上拉过程
432 | scrollBy(0, -distance);
433 | }
434 |
435 | curY = getScrollY();
436 | int slop = mInitScrollY / 2;
437 | //
438 | if (curY > 0 && curY < slop) {
439 | mCurrentStatus = STATUS_RELEASE_TO_REFRESH;
440 | } else if (curY > 0 && curY > slop) {
441 | mCurrentStatus = STATUS_PULL_TO_REFRESH;
442 | }
443 | }
444 |
445 | /**
446 | * 旋转箭头图标
447 | */
448 | protected void rotateHeaderArrow() {
449 |
450 | if (mCurrentStatus == STATUS_REFRESHING) {
451 | return;
452 | } else if (mCurrentStatus == STATUS_PULL_TO_REFRESH && !isArrowUp) {
453 | return;
454 | } else if (mCurrentStatus == STATUS_RELEASE_TO_REFRESH && isArrowUp) {
455 | return;
456 | }
457 |
458 | mProgressBar.setVisibility(View.GONE);
459 | mArrowImageView.setVisibility(View.VISIBLE);
460 | float pivotX = mArrowImageView.getWidth() / 2f;
461 | float pivotY = mArrowImageView.getHeight() / 2f;
462 | float fromDegrees = 0f;
463 | float toDegrees = 0f;
464 | if (mCurrentStatus == STATUS_PULL_TO_REFRESH) {
465 | fromDegrees = 180f;
466 | toDegrees = 360f;
467 | } else if (mCurrentStatus == STATUS_RELEASE_TO_REFRESH) {
468 | fromDegrees = 0f;
469 | toDegrees = 180f;
470 | }
471 |
472 | RotateAnimation animation = new RotateAnimation(fromDegrees, toDegrees, pivotX, pivotY);
473 | animation.setDuration(100);
474 | animation.setFillAfter(true);
475 | mArrowImageView.startAnimation(animation);
476 |
477 | if (mCurrentStatus == STATUS_RELEASE_TO_REFRESH) {
478 | isArrowUp = true;
479 | } else {
480 | isArrowUp = false;
481 | }
482 | }
483 |
484 | /**
485 | * 根据当前状态修改header view中的文本标签
486 | */
487 | protected void changeTips() {
488 | if (mCurrentStatus == STATUS_PULL_TO_REFRESH) {
489 | mTipsTextView.setText(R.string.pull_to_refresh_pull_label);
490 | } else if (mCurrentStatus == STATUS_RELEASE_TO_REFRESH) {
491 | mTipsTextView.setText(R.string.pull_to_refresh_release_label);
492 | }
493 | }
494 |
495 | /**
496 | * 设置下拉刷新监听器
497 | *
498 | * @param listener
499 | */
500 | public void setOnRefreshListener(OnRefreshListener listener) {
501 | mOnRefreshListener = listener;
502 | }
503 |
504 | /**
505 | * 设置滑动到底部时自动加载更多的监听器
506 | *
507 | * @param listener
508 | */
509 | public void setOnLoadListener(OnLoadListener listener) {
510 | mLoadListener = listener;
511 | }
512 |
513 | /**
514 | * 刷新结束,恢复状态
515 | */
516 | public void refreshComplete() {
517 | mScroller.startScroll(getScrollX(), getScrollY(), 0, mInitScrollY - getScrollY());
518 | mCurrentStatus = STATUS_IDLE;
519 | invalidate();
520 | updateHeaderTimeStamp();
521 |
522 | // 200毫秒后处理arrow和progressbar,免得太突兀
523 | this.postDelayed(new Runnable() {
524 |
525 | @Override
526 | public void run() {
527 | mArrowImageView.setVisibility(View.VISIBLE);
528 | mProgressBar.setVisibility(View.GONE);
529 | }
530 | }, 100);
531 |
532 | }
533 |
534 | /**
535 | * 加载结束,恢复状态
536 | */
537 | public void loadCompelte() {
538 | // 隐藏footer
539 | startScroll(mInitScrollY - getScrollY());
540 | mCurrentStatus = STATUS_IDLE;
541 | }
542 |
543 | /**
544 | * 手指抬起时,根据用户下拉的高度来判断是否是有效的下拉刷新操作。如果下拉的距离超过header view的
545 | * 1/2那么则认为是有效的下拉刷新操作,否则恢复原来的视图状态.
546 | */
547 | private void changeHeaderViewStaus() {
548 | int curScrollY = getScrollY();
549 | // 超过1/2则认为是有效的下拉刷新, 否则还原
550 | if (curScrollY < mInitScrollY / 2) {
551 | mScroller.startScroll(getScrollX(), curScrollY, 0, mHeaderView.getPaddingTop()
552 | - curScrollY);
553 | mCurrentStatus = STATUS_REFRESHING;
554 | mTipsTextView.setText(R.string.pull_to_refresh_refreshing_label);
555 | mArrowImageView.clearAnimation();
556 | mArrowImageView.setVisibility(View.GONE);
557 | mProgressBar.setVisibility(View.VISIBLE);
558 | } else {
559 | mScroller.startScroll(getScrollX(), curScrollY, 0, mInitScrollY - curScrollY);
560 | mCurrentStatus = STATUS_IDLE;
561 | }
562 |
563 | invalidate();
564 | }
565 |
566 | /**
567 | * 执行下拉刷新
568 | */
569 | protected void doRefresh() {
570 | changeHeaderViewStaus();
571 | // 执行刷新操作
572 | if (mCurrentStatus == STATUS_REFRESHING && mOnRefreshListener != null) {
573 | mOnRefreshListener.onRefresh();
574 | }
575 | }
576 |
577 | /**
578 | * 修改header上的最近更新时间
579 | */
580 | private void updateHeaderTimeStamp() {
581 | // 设置更新时间
582 | mTimeTextView.setText(R.string.pull_to_refresh_update_time_label);
583 | SimpleDateFormat sdf = (SimpleDateFormat) SimpleDateFormat.getInstance();
584 | sdf.applyPattern("yyyy-MM-dd HH:mm:ss");
585 | mTimeTextView.append(sdf.format(new Date()));
586 | }
587 |
588 | /**
589 | * 返回Content View
590 | *
591 | * @return
592 | */
593 | public T getContentView() {
594 | return mContentView;
595 | }
596 |
597 | /**
598 | * @return
599 | */
600 | public View getHeaderView() {
601 | return mHeaderView;
602 | }
603 |
604 | /**
605 | * @return
606 | */
607 | public View getFooterView() {
608 | return mFooterView;
609 | }
610 |
611 | @Override
612 | public void onScrollStateChanged(AbsListView view, int scrollState) {
613 |
614 | }
615 |
616 | /*
617 | * 滚动监听,当滚动到最底部,且用户设置了加载更多的监听器时触发加载更多操作.
618 | * @see android.widget.AbsListView.OnScrollListener#onScroll(android.widget.
619 | * AbsListView, int, int, int)
620 | */
621 | @Override
622 | public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
623 | int totalItemCount) {
624 | // 用户设置了加载更多监听器,且到了最底部,并且是上拉操作,那么执行加载更多.
625 | if (mLoadListener != null && isBottom() && mScroller.getCurrY() <= mInitScrollY
626 | && mYOffset <= 0
627 | && mCurrentStatus == STATUS_IDLE) {
628 | showFooterView();
629 | doLoadMore();
630 | }
631 | }
632 |
633 | /**
634 | * 执行下拉(自动)加载更多的操作
635 | */
636 | protected void doLoadMore() {
637 | if (mLoadListener != null) {
638 | mLoadListener.onLoadMore();
639 | }
640 | }
641 |
642 | /**
643 | * 显示footer view
644 | */
645 | private void showFooterView() {
646 | startScroll(mFooterView.getMeasuredHeight());
647 | mCurrentStatus = STATUS_LOADING;
648 | }
649 |
650 | }
651 |
--------------------------------------------------------------------------------
/src/com/uit/pullrefresh/base/PullRefreshBase.java:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * created by Mr.Simple, Sep 10, 20146:16:09 PM.
4 | * Copyright (c) 2014, hehonghui@umeng.com All Rights Reserved.
5 | *
6 | * #####################################################
7 | * # #
8 | * # _oo0oo_ #
9 | * # o8888888o #
10 | * # 88" . "88 #
11 | * # (| -_- |) #
12 | * # 0\ = /0 #
13 | * # ___/`---'\___ #
14 | * # .' \\| |# '. #
15 | * # / \\||| : |||# \ #
16 | * # / _||||| -:- |||||- \ #
17 | * # | | \\\ - #/ | | #
18 | * # | \_| ''\---/'' |_/ | #
19 | * # \ .-\__ '-' ___/-. / #
20 | * # ___'. .' /--.--\ `. .'___ #
21 | * # ."" '< `.___\_<|>_/___.' >' "". #
22 | * # | | : `- \`.;`\ _ /`;.`/ - ` : | | #
23 | * # \ \ `_. \_ __\ /__ _/ .-` / / #
24 | * # =====`-.____`.___ \_____/___.-`___.-'===== #
25 | * # `=---=' #
26 | * # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #
27 | * # #
28 | * # 佛祖保佑 永无BUG #
29 | * # #
30 | * #####################################################
31 | */
32 |
33 | package com.uit.pullrefresh.base;
34 |
35 | import android.content.Context;
36 | import android.graphics.Color;
37 | import android.os.AsyncTask;
38 | import android.support.v4.view.MotionEventCompat;
39 | import android.util.AttributeSet;
40 | import android.util.Log;
41 | import android.view.LayoutInflater;
42 | import android.view.MotionEvent;
43 | import android.view.View;
44 | import android.view.ViewConfiguration;
45 | import android.view.ViewGroup;
46 | import android.view.animation.RotateAnimation;
47 | import android.widget.AbsListView;
48 | import android.widget.AbsListView.OnScrollListener;
49 | import android.widget.ImageView;
50 | import android.widget.LinearLayout;
51 | import android.widget.ProgressBar;
52 | import android.widget.TextView;
53 |
54 | import com.uit.pullrefresh.R;
55 | import com.uit.pullrefresh.listener.OnLoadListener;
56 | import com.uit.pullrefresh.listener.OnRefreshListener;
57 |
58 | import java.text.SimpleDateFormat;
59 | import java.util.Date;
60 |
61 | /**
62 | * @author mrsimple
63 | * @param
64 | */
65 | public abstract class PullRefreshBase extends LinearLayout implements
66 | OnScrollListener {
67 |
68 | /**
69 | * 内容视图, 比如ListView, GridView等
70 | */
71 | protected T mContentView;
72 |
73 | /**
74 | * 下拉头视图,因为要修改其padding,所以类型为ViewGroup类型
75 | */
76 | protected ViewGroup mHeaderView;
77 |
78 | /**
79 | * footer视图,因为要修改其padding,所以类型为ViewGroup类型
80 | */
81 | protected ViewGroup mFooterView;
82 | /**
83 | * 下拉刷新监听器
84 | */
85 | protected OnRefreshListener mPullRefreshListener;
86 | /**
87 | * 滑动到底部则自动加载的监听器
88 | */
89 | protected OnLoadListener mLoadMoreListener;
90 |
91 | /**
92 | * LayoutInflater
93 | */
94 | protected LayoutInflater mInflater;
95 |
96 | /**
97 | * Header 的高度
98 | */
99 | protected int mHeaderViewHeight;
100 |
101 | /**
102 | * 空闲状态
103 | */
104 | public static final int STATUS_IDLE = 0;
105 |
106 | /**
107 | * 下拉或者上拉状态
108 | */
109 | public static final int STATUS_PULL_TO_REFRESH = 1;
110 |
111 | /**
112 | * 下拉或者上拉状态
113 | */
114 | public static final int STATUS_RELEASE_TO_REFRESH = 2;
115 | /**
116 | * 刷新中
117 | */
118 | public static final int STATUS_REFRESHING = 3;
119 |
120 | /**
121 | * LOADING中
122 | */
123 | public static final int STATUS_LOADING = 4;
124 |
125 | /**
126 | * 当前状态
127 | */
128 | protected int mCurrentStatus = STATUS_IDLE;
129 |
130 | /**
131 | * Y轴上滑动的距离
132 | */
133 | protected int mYDistance = 0;
134 | /**
135 | * 滑动的距离阀值,超过这个阀值则认为是有效滑动
136 | */
137 | protected int mTouchSlop = 0;
138 | /**
139 | * 触摸事件按下的y坐标
140 | */
141 | protected int mYDown = 0;
142 |
143 | /**
144 | * header view里面的进度条
145 | */
146 | protected ProgressBar mHeaderProgressBar;
147 | /**
148 | * 下拉头的箭头
149 | */
150 | protected ImageView mArrowImageView;
151 | /**
152 | * 箭头图标是否是向上的状态
153 | */
154 | protected boolean isArrowUp = false;
155 | /**
156 | * 下拉刷新的文字TextView
157 | */
158 | protected TextView mTipsTextView;
159 | /**
160 | * 更新时间的文字TextView
161 | */
162 | protected TextView mTimeTextView;
163 | /**
164 | * footer view's progress bar
165 | */
166 | protected ProgressBar mFooterProgressBar;
167 | /**
168 | * footer view's text
169 | */
170 | protected TextView mFooterTextView;
171 | /**
172 | * footer view's height
173 | */
174 | protected int mFooterHeight;
175 | /**
176 | * 屏幕高度
177 | */
178 | protected int mScrHeight = 0;
179 |
180 | /**
181 | * @param context
182 | */
183 | public PullRefreshBase(Context context) {
184 | this(context, null);
185 | }
186 |
187 | /**
188 | * @param context
189 | * @param attrs
190 | */
191 | public PullRefreshBase(Context context, AttributeSet attrs) {
192 |
193 | super(context, attrs);
194 |
195 | mInflater = LayoutInflater.from(context);
196 | setOrientation(LinearLayout.VERTICAL);
197 | initLayout(context);
198 | }
199 |
200 | /**
201 | * 初始化整体布局, header view放在第一个,然后是content view 和 footer view .其中content view的
202 | * 宽度和高度都为match parent .
203 | */
204 | protected final void initLayout(Context context) {
205 |
206 | // 初始化header view
207 | initHeaderView();
208 |
209 | // 初始化 content view
210 | initContentView();
211 | setContentView(mContentView);
212 |
213 | // 初始化 footer
214 | initFooterView();
215 |
216 | //
217 | mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
218 | //
219 | mScrHeight = context.getResources().getDisplayMetrics().heightPixels;
220 |
221 | }
222 |
223 | /**
224 | * 初始化header view
225 | */
226 | protected void initHeaderView() {
227 | //
228 | mHeaderView = (ViewGroup) mInflater.inflate(R.layout.pull_to_refresh_header,
229 | null);
230 | mHeaderView.setBackgroundColor(Color.RED);
231 |
232 | mHeaderProgressBar = (ProgressBar) mHeaderView.findViewById(R.id.pull_to_refresh_progress);
233 | mArrowImageView = (ImageView) mHeaderView.findViewById(R.id.pull_to_arrow_image);
234 | mTipsTextView = (TextView) mHeaderView.findViewById(R.id.pull_to_refresh_text);
235 | mTimeTextView = (TextView) mHeaderView.findViewById(R.id.pull_to_refresh_updated_at);
236 | // add header view to parent
237 | this.addView(mHeaderView, 0);
238 |
239 | }
240 |
241 | /**
242 | * 初始化footer view
243 | */
244 | protected void initFooterView() {
245 |
246 | mFooterView = (ViewGroup) mInflater.inflate(R.layout.pull_to_refresh_footer, null);
247 |
248 | mFooterProgressBar = (ProgressBar) mFooterView.findViewById(R.id.pull_to_loading_progress);
249 | mFooterTextView = (TextView) mFooterView.findViewById(R.id.pull_to_loading_text);
250 | this.addView(mFooterView, 2);
251 | }
252 |
253 | /*
254 | * 获取header view, footer view的高度
255 | * @see android.widget.LinearLayout#onLayout(boolean, int, int, int, int)
256 | */
257 | @Override
258 | protected void onLayout(boolean changed, int l, int t, int r, int b) {
259 | super.onLayout(changed, l, t, r, b);
260 |
261 | if (changed && mHeaderViewHeight <= 0) {
262 | mHeaderViewHeight = mHeaderView.getHeight();
263 | // padding
264 | adjustHeaderPadding(-mHeaderViewHeight);
265 |
266 | mFooterHeight = mFooterView.getHeight();
267 | adjustFooterPadding(-mFooterHeight);
268 | }
269 | }
270 |
271 | /**
272 | * 子类必须实现这个方法,并且在该方法中初始化mContentView字段,即你要显示的主视图.
273 | * 例如PullRefreshListView的mContentView就是ListView
274 | */
275 | protected abstract void initContentView();
276 |
277 | /**
278 | * @param view
279 | */
280 | public void setContentView(T view) {
281 | mContentView = view;
282 | LinearLayout.LayoutParams lvLayoutParams = (LinearLayout.LayoutParams) mContentView
283 | .getLayoutParams();
284 | if (lvLayoutParams == null) {
285 | lvLayoutParams = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
286 | ViewGroup.LayoutParams.MATCH_PARENT);
287 | }
288 | lvLayoutParams.bottomMargin = 0;
289 | lvLayoutParams.weight = 1.0f;
290 | mContentView.setLayoutParams(lvLayoutParams);
291 | this.addView(mContentView, 1);
292 | }
293 |
294 | /**
295 | * @return
296 | */
297 | public T getContentView() {
298 | return mContentView;
299 | }
300 |
301 | /*
302 | * 在适当的时候拦截触摸事件,这里指的适当的时候是当mContentView滑动到顶部,并且是下拉时拦截触摸事件,否则不拦截,交给其child
303 | * view 来处理。
304 | * @see
305 | * android.view.ViewGroup#onInterceptTouchEvent(android.view.MotionEvent)
306 | */
307 | @Override
308 | public boolean onInterceptTouchEvent(MotionEvent ev) {
309 |
310 | /*
311 | * This method JUST determines whether we want to intercept the motion.
312 | * If we return true, onTouchEvent will be called and we do the actual
313 | * scrolling there.
314 | */
315 | final int action = MotionEventCompat.getActionMasked(ev);
316 | // Always handle the case of the touch gesture being complete.
317 | if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
318 | // Do not intercept touch event, let the child handle it
319 | return false;
320 | }
321 |
322 | switch (action) {
323 |
324 | case MotionEvent.ACTION_DOWN:
325 | mYDown = (int) ev.getRawY();
326 | break;
327 |
328 | case MotionEvent.ACTION_MOVE:
329 | // int yDistance = (int) ev.getRawY() - mYDown;
330 | mYDistance = (int) ev.getRawY() - mYDown;
331 | showStatus(mCurrentStatus);
332 | Log.d(VIEW_LOG_TAG, "%%% isBottom : " + isBottom() + ", isTop : " + isTop()
333 | + ", mYDistance : " + mYDistance);
334 | // 如果拉到了顶部, 并且是下拉,则拦截触摸事件,从而转到onTouchEvent来处理下拉刷新事件
335 | if ((isTop() && mYDistance > 0)
336 | || (mYDistance > 0 && mCurrentStatus == STATUS_REFRESHING)) {
337 | Log.d(VIEW_LOG_TAG, "--------- mYDistance : " + mYDistance);
338 | return true;
339 | }
340 | break;
341 |
342 | }
343 |
344 | // Do not intercept touch event, let the child handle it
345 | return false;
346 | }
347 |
348 | /**
349 | * @param status
350 | */
351 | private void showStatus(int status) {
352 | String statusString = "";
353 | if (status == STATUS_IDLE) {
354 | statusString = "idle";
355 | } else if (status == STATUS_PULL_TO_REFRESH) {
356 | statusString = "pull to refresh";
357 | } else if (status == STATUS_RELEASE_TO_REFRESH) {
358 | statusString = "release to refresh";
359 | }
360 | else if (status == STATUS_REFRESHING) {
361 | statusString = "refreshing";
362 | }
363 | Log.d(VIEW_LOG_TAG, "### status = " + statusString);
364 | }
365 |
366 | /*
367 | * 在这里处理触摸事件以达到下拉刷新或者上拉自动加载的问题
368 | * @see android.view.View#onTouchEvent(android.view.MotionEvent)
369 | */
370 | @Override
371 | public boolean onTouchEvent(MotionEvent event) {
372 |
373 | Log.d(VIEW_LOG_TAG, "@@@ onTouchEvent : action = " + event.getAction());
374 | switch (event.getAction()) {
375 | case MotionEvent.ACTION_DOWN:
376 | mYDown = (int) event.getRawY();
377 | Log.d(VIEW_LOG_TAG, "#### ACTION_DOWN");
378 | break;
379 |
380 | case MotionEvent.ACTION_MOVE:
381 | Log.d(VIEW_LOG_TAG, "#### ACTION_MOVE");
382 | int currentY = (int) event.getRawY();
383 | mYDistance = currentY - mYDown;
384 |
385 | Log.d(VIEW_LOG_TAG, "### touch slop = " + mTouchSlop + ", distance = " + mYDistance);
386 | showStatus(mCurrentStatus);
387 | // 高度大于header view的高度才可以刷新
388 | if (Math.abs(mYDistance) >= mTouchSlop) {
389 | if (mCurrentStatus != STATUS_REFRESHING) {
390 | //
391 | if (mHeaderView.getPaddingTop() > mHeaderViewHeight * 0.7f) {
392 | mCurrentStatus = STATUS_RELEASE_TO_REFRESH;
393 | mTipsTextView.setText(R.string.pull_to_refresh_release_label);
394 | } else {
395 | mCurrentStatus = STATUS_PULL_TO_REFRESH;
396 | mTipsTextView.setText(R.string.pull_to_refresh_pull_label);
397 | }
398 | }
399 |
400 | rotateHeaderArrow();
401 | // 对滑动距离取了80%
402 | int scaleHeight = (int) (mYDistance * 0.8f);
403 | // 滑动的距离小于屏幕高度4分之一时才拉伸header, 否则保持不变
404 | if (scaleHeight <= mScrHeight / 4) {
405 | adjustHeaderPadding(scaleHeight);
406 | }
407 | }
408 |
409 | break;
410 |
411 | case MotionEvent.ACTION_UP:
412 | // 下拉刷新的具体操作
413 | doRefresh();
414 | break;
415 | default:
416 | break;
417 |
418 | }
419 |
420 | return true;
421 | }
422 |
423 | /**
424 | * 执行刷新操作
425 | */
426 | private final void doRefresh() {
427 | if (mCurrentStatus == STATUS_RELEASE_TO_REFRESH) {
428 | // 设置为正在刷新的状态
429 | mCurrentStatus = STATUS_REFRESHING;
430 | mArrowImageView.clearAnimation();
431 | // 隐藏header中的箭头图标
432 | mArrowImageView.setVisibility(View.GONE);
433 | // 设置header中的进度条可见
434 | mHeaderProgressBar.setVisibility(View.VISIBLE);
435 | // 设置一些文本参数
436 | mTimeTextView.setText(R.string.pull_to_refresh_update_time_label);
437 | SimpleDateFormat sdf = new SimpleDateFormat();
438 | mTimeTextView.append(sdf.format(new Date()));
439 | //
440 | mTipsTextView.setText(R.string.pull_to_refresh_refreshing_label);
441 |
442 | // 执行回调
443 | if (mPullRefreshListener != null) {
444 | mPullRefreshListener.onRefresh();
445 | }
446 | // 使headview 正常显示, 直到调用了refreshComplete后再隐藏
447 | new HeaderViewHideTask().execute(0);
448 |
449 | } else if (mCurrentStatus == STATUS_REFRESHING) {
450 | // 使headview 正常显示, 直到调用了refreshComplete后再隐藏
451 | new HeaderViewHideTask().execute(0);
452 | } else {
453 | // 隐藏header view
454 | hideHeaderView();
455 | }
456 | }
457 |
458 | /**
459 | * 下拉到底部时加载更多
460 | */
461 | private void loadmore() {
462 | if (isShowFooterView() && mLoadMoreListener != null) {
463 | mFooterTextView.setText(R.string.pull_to_refresh_refreshing_label);
464 | mFooterProgressBar.setVisibility(View.VISIBLE);
465 | adjustFooterPadding(0);
466 | mCurrentStatus = STATUS_LOADING;
467 | mLoadMoreListener.onLoadMore();
468 | }
469 | }
470 |
471 | /**
472 | * @param listener
473 | */
474 | public void setOnRefreshListener(OnRefreshListener listener) {
475 | mPullRefreshListener = listener;
476 | }
477 |
478 | /**
479 | * @param listener
480 | */
481 | public void setOnLoadMoreListener(OnLoadListener listener) {
482 | mLoadMoreListener = listener;
483 | }
484 |
485 | /**
486 | * 刷新结束,在调用完onRefresh后调用,否则header view会一直显示.
487 | */
488 | public void refreshComplete() {
489 | mCurrentStatus = STATUS_IDLE;
490 | mHeaderProgressBar.setVisibility(View.GONE);
491 | mArrowImageView.setVisibility(View.GONE);
492 | hideHeaderView();
493 | }
494 |
495 | /**
496 | * 隐藏header view
497 | */
498 | protected void hideHeaderView() {
499 | new HeaderViewHideTask().execute(-mHeaderViewHeight);
500 | }
501 |
502 | /**
503 | * 旋转箭头图标
504 | */
505 | protected void rotateHeaderArrow() {
506 |
507 | if (mCurrentStatus == STATUS_REFRESHING) {
508 | return;
509 | } else if (mCurrentStatus == STATUS_PULL_TO_REFRESH && !isArrowUp) {
510 | return;
511 | } else if (mCurrentStatus == STATUS_RELEASE_TO_REFRESH && isArrowUp) {
512 | return;
513 | }
514 |
515 | mArrowImageView.setVisibility(View.VISIBLE);
516 | Log.d(VIEW_LOG_TAG, "------ rotateHeaderArrow");
517 | float pivotX = mArrowImageView.getWidth() / 2f;
518 | float pivotY = mArrowImageView.getHeight() / 2f;
519 | float fromDegrees = 0f;
520 | float toDegrees = 0f;
521 | if (mCurrentStatus == STATUS_PULL_TO_REFRESH) {
522 | fromDegrees = 180f;
523 | toDegrees = 360f;
524 | } else if (mCurrentStatus == STATUS_RELEASE_TO_REFRESH) {
525 | fromDegrees = 0f;
526 | toDegrees = 180f;
527 | }
528 |
529 | RotateAnimation animation = new RotateAnimation(fromDegrees, toDegrees, pivotX, pivotY);
530 | animation.setDuration(100);
531 | animation.setFillAfter(true);
532 | mArrowImageView.startAnimation(animation);
533 |
534 | if (mCurrentStatus == STATUS_RELEASE_TO_REFRESH) {
535 | isArrowUp = true;
536 | } else {
537 | isArrowUp = false;
538 | }
539 | }
540 |
541 | /**
542 | * 隐藏footer view
543 | */
544 | protected void hideFooterView() {
545 | adjustFooterPadding(-mFooterHeight);
546 | }
547 |
548 | /**
549 | * 上拉加载结束
550 | */
551 | public void loadMoreComplete() {
552 | mCurrentStatus = STATUS_IDLE;
553 | mFooterTextView.setText(R.string.pull_to_refresh_load_label);
554 | mFooterProgressBar.setVisibility(View.GONE);
555 | // adjustFooterPadding(-mFooterHeight);
556 | // hideFooterView();
557 | new FooterViewTask().execute(-mFooterHeight);
558 | }
559 |
560 | /**
561 | * 调整header view的bottom padding
562 | *
563 | * @param bottomPadding
564 | */
565 | private void adjustFooterPadding(int bottomPadding) {
566 | mFooterView.setPadding(mFooterView.getPaddingLeft(), 0,
567 | mFooterView.getPaddingRight(), bottomPadding);
568 | }
569 |
570 | /**
571 | * 调整header view的top padding
572 | *
573 | * @param topPadding
574 | */
575 | private void adjustHeaderPadding(int topPadding) {
576 | mHeaderView.setPadding(mHeaderView.getPaddingLeft(), topPadding,
577 | mHeaderView.getPaddingRight(), 0);
578 | }
579 |
580 | @Override
581 | public void onScrollStateChanged(AbsListView view, int scrollState) {
582 |
583 | }
584 |
585 | /*
586 | * 滚动事件,实现滑动到底部时上拉加载更多
587 | * @see android.widget.AbsListView.OnScrollListener#onScroll(android.widget.
588 | * AbsListView, int, int, int)
589 | */
590 | @Override
591 | public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
592 | int totalItemCount) {
593 |
594 | Log.d(VIEW_LOG_TAG, "&&& mYDistance = " + mYDistance);
595 | if (mFooterView == null || mYDistance >= 0 || mCurrentStatus == STATUS_LOADING
596 | || mCurrentStatus == STATUS_REFRESHING) {
597 | return;
598 | }
599 |
600 | loadmore();
601 | }
602 |
603 | /**
604 | * 是否可以下拉刷新了, 即是否拉到了头部
605 | *
606 | * @return
607 | */
608 | protected abstract boolean isTop();
609 |
610 | /**
611 | * 下拉到底部时加载更多
612 | *
613 | * @return
614 | */
615 | protected boolean isBottom() {
616 | return false;
617 | }
618 |
619 | /**
620 | * 是否到了显示footer view的时刻,该方法在onScroll中调用。在这个类中实现了mScroll方法,
621 | * 在设置mContentView时会将this设置给mContentView,以此监听mContentView的滑动事件.
622 | * 因此如果需要支持上拉加载更多则mContentView必须支持setOnScrollListener方法
623 | * ,并且在初始化mContentView时调用该方法进行注册.
624 | *
625 | * @return
626 | */
627 | protected boolean isShowFooterView() {
628 | return false;
629 | }
630 |
631 | /**
632 | * 隐藏header view的异步任务, 实现平滑隐藏
633 | *
634 | * @author mrsimple
635 | */
636 | class HeaderViewHideTask extends AsyncTask {
637 |
638 | @Override
639 | protected Void doInBackground(Integer... params) {
640 | int curPaddingTop = mHeaderView.getPaddingTop();
641 | int targetPadding = params[0];
642 | int step = 1;
643 | Log.d(VIEW_LOG_TAG, "%%% new : curPaddingTop =" + curPaddingTop + ", targetPadding = "
644 | + targetPadding);
645 | int mode = curPaddingTop % step;
646 |
647 | try {
648 | do {
649 | if (mHeaderView.getPaddingTop() <= targetPadding) {
650 | break;
651 | }
652 |
653 | if (curPaddingTop - mode == targetPadding) {
654 | if (mode != 0) {
655 | curPaddingTop -= mode;
656 | } else {
657 | curPaddingTop = targetPadding;
658 | }
659 | } else {
660 | curPaddingTop -= step;
661 | }
662 | publishProgress(curPaddingTop);
663 | Log.d(VIEW_LOG_TAG, "%%% curPaddingTop = " + curPaddingTop + ", mode = " + mode
664 | + ", target = "
665 | + targetPadding);
666 | Thread.sleep(1);
667 | } while (true);
668 | } catch (InterruptedException e) {
669 | e.printStackTrace();
670 | }
671 | return null;
672 | }
673 |
674 | @Override
675 | protected void onProgressUpdate(Integer... values) {
676 | adjustHeaderPadding(values[0]);
677 | }
678 |
679 | } // end of HeaderViewHideTask
680 |
681 | /**
682 | * 隐藏header view的异步任务, 实现平滑隐藏
683 | *
684 | * @author mrsimple
685 | */
686 | class FooterViewTask extends AsyncTask {
687 |
688 | @Override
689 | protected Void doInBackground(Integer... params) {
690 | // 0
691 | int curPaddingBottom = mFooterView.getPaddingBottom();
692 | // -footer height
693 | int targetPadding = params[0];
694 | int step = 1;
695 | Log.d(VIEW_LOG_TAG, "%%% new : curPaddingTop =" + curPaddingBottom
696 | + ", targetPadding = "
697 | + targetPadding);
698 | try {
699 | do {
700 |
701 | if (curPaddingBottom == targetPadding || curPaddingBottom > 0
702 | || curPaddingBottom < -mFooterHeight) {
703 | break;
704 | }
705 |
706 | if (targetPadding == 0) {
707 | curPaddingBottom += step;
708 | } else if (targetPadding < 0) {
709 | curPaddingBottom -= step;
710 | }
711 | publishProgress(curPaddingBottom);
712 |
713 | Log.d(VIEW_LOG_TAG, "%%% curPaddingTop = " + curPaddingBottom
714 | + ", target = "
715 | + targetPadding);
716 | Thread.sleep(1);
717 | } while (true);
718 | } catch (InterruptedException e) {
719 | e.printStackTrace();
720 | }
721 | return null;
722 | }
723 |
724 | @Override
725 | protected void onProgressUpdate(Integer... values) {
726 | adjustFooterPadding(values[0]);
727 | }
728 |
729 | } // end of HeaderViewHideTask
730 |
731 | }
732 |
--------------------------------------------------------------------------------