├── .classpath
├── .project
├── AndroidManifest.xml
├── README.md
├── ic_launcher-web.png
├── libs
└── android-support-v4.jar
├── proguard-project.txt
├── project.properties
├── res
├── anim
│ ├── loading_anim.xml
│ ├── reverse_anim.xml
│ └── rotating.xml
├── drawable-hdpi
│ └── ic_launcher.png
├── drawable-mdpi
│ └── ic_launcher.png
├── drawable-xhdpi
│ ├── ic_launcher.png
│ ├── loading.png
│ ├── loading_01.png
│ ├── loading_02.png
│ ├── loading_03.png
│ ├── loading_04.png
│ ├── loading_05.png
│ ├── loading_06.png
│ ├── loading_07.png
│ ├── loading_08.png
│ ├── loading_09.png
│ ├── loading_10.png
│ ├── loading_11.png
│ ├── loading_12.png
│ ├── pull_icon_big.png
│ ├── refresh_failed.png
│ ├── refresh_succeed.png
│ └── refreshing.png
├── drawable-xxhdpi
│ └── ic_launcher.png
├── layout
│ ├── activity_listview.xml
│ ├── list_item_layout.xml
│ ├── load_more.xml
│ └── refresh_head.xml
├── values-sw600dp
│ └── dimens.xml
├── values-sw720dp-land
│ └── dimens.xml
└── values
│ ├── color.xml
│ ├── dimens.xml
│ ├── strings.xml
│ └── styles.xml
├── screenshot.gif
└── src
└── com
└── jingchen
└── autoload
├── MainActivity.java
├── MyAdapter.java
├── MyListener.java
├── PullToRefreshLayout.java
├── Pullable.java
└── PullableListView.java
/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | AutoLoad
4 |
5 |
6 |
7 |
8 |
9 | com.android.ide.eclipse.adt.ResourceManagerBuilder
10 |
11 |
12 |
13 |
14 | com.android.ide.eclipse.adt.PreCompilerBuilder
15 |
16 |
17 |
18 |
19 | org.eclipse.jdt.core.javabuilder
20 |
21 |
22 |
23 |
24 | com.android.ide.eclipse.adt.ApkBuilder
25 |
26 |
27 |
28 |
29 |
30 | com.android.ide.eclipse.adt.AndroidNature
31 | org.eclipse.jdt.core.javanature
32 |
33 |
34 |
--------------------------------------------------------------------------------
/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
16 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | AutoLoad
2 | ========
3 |
4 | 滑动到底部自动加载的ListView,实现原理参见博客[http://blog.csdn.net/zhongkejingwang/article/details/38963177](http://blog.csdn.net/zhongkejingwang/article/details/38963177)
5 | ###效果图
6 | 
7 |
--------------------------------------------------------------------------------
/ic_launcher-web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jingchenUSTC/AutoLoad/8b89a9bc6b318a28976035005ff7fa68db6d3a7a/ic_launcher-web.png
--------------------------------------------------------------------------------
/libs/android-support-v4.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jingchenUSTC/AutoLoad/8b89a9bc6b318a28976035005ff7fa68db6d3a7a/libs/android-support-v4.jar
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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-8
15 |
--------------------------------------------------------------------------------
/res/anim/loading_anim.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
7 |
10 |
13 |
16 |
19 |
22 |
25 |
28 |
31 |
34 |
37 |
40 |
41 |
--------------------------------------------------------------------------------
/res/anim/reverse_anim.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
--------------------------------------------------------------------------------
/res/anim/rotating.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
--------------------------------------------------------------------------------
/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jingchenUSTC/AutoLoad/8b89a9bc6b318a28976035005ff7fa68db6d3a7a/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jingchenUSTC/AutoLoad/8b89a9bc6b318a28976035005ff7fa68db6d3a7a/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jingchenUSTC/AutoLoad/8b89a9bc6b318a28976035005ff7fa68db6d3a7a/res/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/loading.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jingchenUSTC/AutoLoad/8b89a9bc6b318a28976035005ff7fa68db6d3a7a/res/drawable-xhdpi/loading.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/loading_01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jingchenUSTC/AutoLoad/8b89a9bc6b318a28976035005ff7fa68db6d3a7a/res/drawable-xhdpi/loading_01.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/loading_02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jingchenUSTC/AutoLoad/8b89a9bc6b318a28976035005ff7fa68db6d3a7a/res/drawable-xhdpi/loading_02.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/loading_03.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jingchenUSTC/AutoLoad/8b89a9bc6b318a28976035005ff7fa68db6d3a7a/res/drawable-xhdpi/loading_03.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/loading_04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jingchenUSTC/AutoLoad/8b89a9bc6b318a28976035005ff7fa68db6d3a7a/res/drawable-xhdpi/loading_04.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/loading_05.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jingchenUSTC/AutoLoad/8b89a9bc6b318a28976035005ff7fa68db6d3a7a/res/drawable-xhdpi/loading_05.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/loading_06.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jingchenUSTC/AutoLoad/8b89a9bc6b318a28976035005ff7fa68db6d3a7a/res/drawable-xhdpi/loading_06.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/loading_07.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jingchenUSTC/AutoLoad/8b89a9bc6b318a28976035005ff7fa68db6d3a7a/res/drawable-xhdpi/loading_07.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/loading_08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jingchenUSTC/AutoLoad/8b89a9bc6b318a28976035005ff7fa68db6d3a7a/res/drawable-xhdpi/loading_08.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/loading_09.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jingchenUSTC/AutoLoad/8b89a9bc6b318a28976035005ff7fa68db6d3a7a/res/drawable-xhdpi/loading_09.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/loading_10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jingchenUSTC/AutoLoad/8b89a9bc6b318a28976035005ff7fa68db6d3a7a/res/drawable-xhdpi/loading_10.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/loading_11.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jingchenUSTC/AutoLoad/8b89a9bc6b318a28976035005ff7fa68db6d3a7a/res/drawable-xhdpi/loading_11.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/loading_12.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jingchenUSTC/AutoLoad/8b89a9bc6b318a28976035005ff7fa68db6d3a7a/res/drawable-xhdpi/loading_12.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/pull_icon_big.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jingchenUSTC/AutoLoad/8b89a9bc6b318a28976035005ff7fa68db6d3a7a/res/drawable-xhdpi/pull_icon_big.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/refresh_failed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jingchenUSTC/AutoLoad/8b89a9bc6b318a28976035005ff7fa68db6d3a7a/res/drawable-xhdpi/refresh_failed.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/refresh_succeed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jingchenUSTC/AutoLoad/8b89a9bc6b318a28976035005ff7fa68db6d3a7a/res/drawable-xhdpi/refresh_succeed.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/refreshing.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jingchenUSTC/AutoLoad/8b89a9bc6b318a28976035005ff7fa68db6d3a7a/res/drawable-xhdpi/refreshing.png
--------------------------------------------------------------------------------
/res/drawable-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jingchenUSTC/AutoLoad/8b89a9bc6b318a28976035005ff7fa68db6d3a7a/res/drawable-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/layout/activity_listview.xml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 |
9 |
16 |
17 |
--------------------------------------------------------------------------------
/res/layout/list_item_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
15 |
16 |
--------------------------------------------------------------------------------
/res/layout/load_more.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
16 |
17 |
26 |
27 |
--------------------------------------------------------------------------------
/res/layout/refresh_head.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
14 |
15 |
19 |
20 |
27 |
28 |
36 |
37 |
45 |
46 |
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/res/values-sw600dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/res/values-sw720dp-land/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 | 128dp
8 |
9 |
10 |
--------------------------------------------------------------------------------
/res/values/color.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFFFFF
4 | #000000
5 | #eeeeee
6 | #6593cb
7 |
8 |
--------------------------------------------------------------------------------
/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 16dp
5 | 16dp
6 |
7 |
8 |
--------------------------------------------------------------------------------
/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 滑到底部自动加载控件
5 | 下拉刷新
6 | 释放立即刷新
7 | 正在刷新...
8 | 刷新成功
9 | 刷新失败
10 | 更多
11 | 正在加载...
12 |
13 |
--------------------------------------------------------------------------------
/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
14 |
15 |
16 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/screenshot.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jingchenUSTC/AutoLoad/8b89a9bc6b318a28976035005ff7fa68db6d3a7a/screenshot.gif
--------------------------------------------------------------------------------
/src/com/jingchen/autoload/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.jingchen.autoload;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | import android.app.Activity;
7 | import android.os.Bundle;
8 | import android.os.Handler;
9 | import android.os.Message;
10 | import android.view.View;
11 | import android.widget.AdapterView;
12 | import android.widget.AdapterView.OnItemClickListener;
13 | import android.widget.AdapterView.OnItemLongClickListener;
14 | import android.widget.Toast;
15 |
16 | import com.jingchen.autoload.PullableListView.OnLoadListener;
17 |
18 | /**
19 | * 更多详解见博客http://blog.csdn.net/zhongkejingwang/article/details/38963177
20 | * @author chenjing
21 | *
22 | */
23 | public class MainActivity extends Activity implements OnLoadListener
24 | {
25 |
26 | private PullableListView listView;
27 | private MyAdapter adapter;
28 |
29 | @Override
30 | protected void onCreate(Bundle savedInstanceState)
31 | {
32 | super.onCreate(savedInstanceState);
33 | setContentView(R.layout.activity_listview);
34 | ((PullToRefreshLayout) findViewById(R.id.refresh_view))
35 | .setOnRefreshListener(new MyListener());
36 | listView = (PullableListView) findViewById(R.id.content_view);
37 | initListView();
38 | listView.setOnLoadListener(this);
39 | }
40 |
41 | /**
42 | * ListView初始化方法
43 | */
44 | private void initListView()
45 | {
46 | List items = new ArrayList();
47 | for (int i = 0; i < 10; i++)
48 | {
49 | items.add("这里是item " + i);
50 | }
51 | adapter = new MyAdapter(this, items);
52 | listView.setAdapter(adapter);
53 | listView.setOnItemLongClickListener(new OnItemLongClickListener()
54 | {
55 |
56 | @Override
57 | public boolean onItemLongClick(AdapterView> parent, View view,
58 | int position, long id)
59 | {
60 | Toast.makeText(
61 | MainActivity.this,
62 | "LongClick on "
63 | + parent.getAdapter().getItemId(position),
64 | Toast.LENGTH_SHORT).show();
65 | return true;
66 | }
67 | });
68 | listView.setOnItemClickListener(new OnItemClickListener()
69 | {
70 |
71 | @Override
72 | public void onItemClick(AdapterView> parent, View view,
73 | int position, long id)
74 | {
75 | Toast.makeText(MainActivity.this,
76 | " Click on " + parent.getAdapter().getItemId(position),
77 | Toast.LENGTH_SHORT).show();
78 | }
79 | });
80 | }
81 |
82 | @Override
83 | public void onLoad(final PullableListView pullableListView)
84 | {
85 | new Handler()
86 | {
87 | @Override
88 | public void handleMessage(Message msg)
89 | {
90 | for (int i = 0; i < 5; i++)
91 | adapter.addItem("这里是自动加载进来的item");
92 | // 千万别忘了告诉控件加载完毕了哦!
93 | pullableListView.finishLoading();
94 | }
95 | }.sendEmptyMessageDelayed(0, 5000);
96 | }
97 |
98 | }
99 |
--------------------------------------------------------------------------------
/src/com/jingchen/autoload/MyAdapter.java:
--------------------------------------------------------------------------------
1 | package com.jingchen.autoload;
2 |
3 | import java.util.List;
4 |
5 | import android.content.Context;
6 | import android.view.LayoutInflater;
7 | import android.view.View;
8 | import android.view.ViewGroup;
9 | import android.widget.BaseAdapter;
10 | import android.widget.TextView;
11 |
12 | public class MyAdapter extends BaseAdapter
13 | {
14 | List items;
15 | Context context;
16 |
17 | public MyAdapter(Context context, List items)
18 | {
19 | this.context = context;
20 | this.items = items;
21 | }
22 |
23 | public void addItem(String text)
24 | {
25 | items.add(text);
26 | notifyDataSetChanged();
27 | }
28 |
29 | @Override
30 | public int getCount()
31 | {
32 | return items.size();
33 | }
34 |
35 | @Override
36 | public Object getItem(int position)
37 | {
38 | return items.get(position);
39 | }
40 |
41 | @Override
42 | public long getItemId(int position)
43 | {
44 | return position;
45 | }
46 |
47 | @Override
48 | public View getView(int position, View convertView, ViewGroup parent)
49 | {
50 | View view = LayoutInflater.from(context).inflate(
51 | R.layout.list_item_layout, null);
52 | TextView tv = (TextView) view.findViewById(R.id.tv);
53 | tv.setText(items.get(position));
54 | return view;
55 | }
56 |
57 | }
58 |
--------------------------------------------------------------------------------
/src/com/jingchen/autoload/MyListener.java:
--------------------------------------------------------------------------------
1 | package com.jingchen.autoload;
2 |
3 | import android.os.Handler;
4 | import android.os.Message;
5 |
6 | import com.jingchen.autoload.PullToRefreshLayout.OnRefreshListener;
7 |
8 | public class MyListener implements OnRefreshListener
9 | {
10 |
11 | @Override
12 | public void onRefresh(final PullToRefreshLayout pullToRefreshLayout)
13 | {
14 | // 下拉刷新操作
15 | new Handler()
16 | {
17 | @Override
18 | public void handleMessage(Message msg)
19 | {
20 | // 千万别忘了告诉控件刷新完毕了哦!
21 | pullToRefreshLayout.refreshFinish(PullToRefreshLayout.SUCCEED);
22 | }
23 | }.sendEmptyMessageDelayed(0, 5000);
24 | }
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/src/com/jingchen/autoload/PullToRefreshLayout.java:
--------------------------------------------------------------------------------
1 | package com.jingchen.autoload;
2 |
3 | import java.util.Timer;
4 | import java.util.TimerTask;
5 |
6 | import android.content.Context;
7 | import android.os.Handler;
8 | import android.os.Message;
9 | import android.util.AttributeSet;
10 | import android.view.MotionEvent;
11 | import android.view.View;
12 | import android.view.ViewGroup;
13 | import android.view.animation.AnimationUtils;
14 | import android.view.animation.LinearInterpolator;
15 | import android.view.animation.RotateAnimation;
16 | import android.widget.RelativeLayout;
17 | import android.widget.TextView;
18 |
19 | /**
20 | * 更多详解见博客http://blog.csdn.net/zhongkejingwang/article/details/38868463
21 | *
22 | * @author 陈靖
23 | */
24 | public class PullToRefreshLayout extends RelativeLayout
25 | {
26 | public static final String TAG = "PullToRefreshLayout";
27 | // 初始状态
28 | public static final int INIT = 0;
29 | // 释放刷新
30 | public static final int RELEASE_TO_REFRESH = 1;
31 | // 正在刷新
32 | public static final int REFRESHING = 2;
33 | // 操作完毕
34 | public static final int DONE = 3;
35 | // 当前状态
36 | private int state = INIT;
37 | // 刷新回调接口
38 | private OnRefreshListener mListener;
39 | // 刷新成功
40 | public static final int SUCCEED = 0;
41 | // 刷新失败
42 | public static final int FAIL = 1;
43 | // 按下Y坐标,上一个事件点Y坐标
44 | private float downY, lastY;
45 |
46 | // 下拉的距离。注意:pullDownY和pullUpY不可能同时不为0
47 | public float pullDownY = 0;
48 |
49 | // 释放刷新的距离
50 | private float refreshDist = 200;
51 |
52 | private MyTimer timer;
53 | // 回滚速度
54 | public float MOVE_SPEED = 8;
55 | // 第一次执行布局
56 | private boolean isLayout = false;
57 | // 在刷新过程中滑动操作
58 | private boolean isTouch = false;
59 | // 手指滑动距离与下拉头的滑动距离比,中间会随正切函数变化
60 | private float radio = 2;
61 |
62 | // 下拉箭头的转180°动画
63 | private RotateAnimation rotateAnimation;
64 | // 均匀旋转动画
65 | private RotateAnimation refreshingAnimation;
66 |
67 | // 下拉头
68 | private View refreshView;
69 | // 下拉的箭头
70 | private View pullView;
71 | // 正在刷新的图标
72 | private View refreshingView;
73 | // 刷新结果图标
74 | private View refreshStateImageView;
75 | // 刷新结果:成功或失败
76 | private TextView refreshStateTextView;
77 |
78 | // 实现了Pullable接口的View
79 | private View pullableView;
80 | // 过滤多点触碰
81 | private int mEvents;
82 |
83 | /**
84 | * 执行自动回滚的handler
85 | */
86 | Handler updateHandler = new Handler()
87 | {
88 |
89 | @Override
90 | public void handleMessage(Message msg)
91 | {
92 | // 回弹速度随下拉距离moveDeltaY增大而增大
93 | MOVE_SPEED = (float) (8 + 5 * Math.tan(Math.PI / 2
94 | / getMeasuredHeight() * (pullDownY)));
95 | if (!isTouch)
96 | {
97 | // 正在刷新,且没有往上推的话则悬停,显示"正在刷新..."
98 | if (state == REFRESHING && pullDownY <= refreshDist)
99 | {
100 | pullDownY = refreshDist;
101 | timer.cancel();
102 | }
103 |
104 | }
105 | if (pullDownY > 0)
106 | pullDownY -= MOVE_SPEED;
107 | if (pullDownY <= 0)
108 | {
109 | // 已完成回弹
110 | pullDownY = 0;
111 | pullView.clearAnimation();
112 | // 隐藏下拉头时有可能还在刷新,只有当前状态不是正在刷新时才改变状态
113 | if (state != REFRESHING)
114 | changeState(INIT);
115 | timer.cancel();
116 | }
117 | // 刷新布局,会自动调用onLayout
118 | requestLayout();
119 | }
120 |
121 | };
122 |
123 | public void setOnRefreshListener(OnRefreshListener listener)
124 | {
125 | mListener = listener;
126 | }
127 |
128 | public PullToRefreshLayout(Context context)
129 | {
130 | super(context);
131 | initView(context);
132 | }
133 |
134 | public PullToRefreshLayout(Context context, AttributeSet attrs)
135 | {
136 | super(context, attrs);
137 | initView(context);
138 | }
139 |
140 | public PullToRefreshLayout(Context context, AttributeSet attrs, int defStyle)
141 | {
142 | super(context, attrs, defStyle);
143 | initView(context);
144 | }
145 |
146 | private void initView(Context context)
147 | {
148 | timer = new MyTimer(updateHandler);
149 | rotateAnimation = (RotateAnimation) AnimationUtils.loadAnimation(
150 | context, R.anim.reverse_anim);
151 | refreshingAnimation = (RotateAnimation) AnimationUtils.loadAnimation(
152 | context, R.anim.rotating);
153 | // 添加匀速转动动画
154 | LinearInterpolator lir = new LinearInterpolator();
155 | rotateAnimation.setInterpolator(lir);
156 | refreshingAnimation.setInterpolator(lir);
157 | }
158 |
159 | private void hide()
160 | {
161 | timer.schedule(5);
162 | }
163 |
164 | /**
165 | * 完成刷新操作,显示刷新结果。注意:刷新完成后一定要调用这个方法
166 | */
167 | /**
168 | * @param refreshResult
169 | * PullToRefreshLayout.SUCCEED代表成功,PullToRefreshLayout.FAIL代表失败
170 | */
171 | public void refreshFinish(int refreshResult)
172 | {
173 | refreshingView.clearAnimation();
174 | refreshingView.setVisibility(View.GONE);
175 | switch (refreshResult)
176 | {
177 | case SUCCEED:
178 | // 刷新成功
179 | refreshStateImageView.setVisibility(View.VISIBLE);
180 | refreshStateTextView.setText(R.string.refresh_succeed);
181 | refreshStateImageView
182 | .setBackgroundResource(R.drawable.refresh_succeed);
183 | break;
184 | case FAIL:
185 | default:
186 | // 刷新失败
187 | refreshStateImageView.setVisibility(View.VISIBLE);
188 | refreshStateTextView.setText(R.string.refresh_fail);
189 | refreshStateImageView
190 | .setBackgroundResource(R.drawable.refresh_failed);
191 | break;
192 | }
193 | // 刷新结果停留1秒
194 | new Handler()
195 | {
196 | @Override
197 | public void handleMessage(Message msg)
198 | {
199 | changeState(DONE);
200 | hide();
201 | }
202 | }.sendEmptyMessageDelayed(0, 1000);
203 | }
204 |
205 | private void changeState(int to)
206 | {
207 | state = to;
208 | switch (state)
209 | {
210 | case INIT:
211 | // 下拉布局初始状态
212 | refreshStateImageView.setVisibility(View.GONE);
213 | refreshStateTextView.setText(R.string.pull_to_refresh);
214 | pullView.clearAnimation();
215 | pullView.setVisibility(View.VISIBLE);
216 | break;
217 | case RELEASE_TO_REFRESH:
218 | // 释放刷新状态
219 | refreshStateTextView.setText(R.string.release_to_refresh);
220 | pullView.startAnimation(rotateAnimation);
221 | break;
222 | case REFRESHING:
223 | // 正在刷新状态
224 | pullView.clearAnimation();
225 | refreshingView.setVisibility(View.VISIBLE);
226 | pullView.setVisibility(View.INVISIBLE);
227 | refreshingView.startAnimation(refreshingAnimation);
228 | refreshStateTextView.setText(R.string.refreshing);
229 | break;
230 | case DONE:
231 | // 刷新完毕,啥都不做
232 | break;
233 | }
234 | }
235 |
236 | /*
237 | * (非 Javadoc)由父控件决定是否分发事件,防止事件冲突
238 | *
239 | * @see android.view.ViewGroup#dispatchTouchEvent(android.view.MotionEvent)
240 | */
241 | @Override
242 | public boolean dispatchTouchEvent(MotionEvent ev)
243 | {
244 | switch (ev.getActionMasked())
245 | {
246 | case MotionEvent.ACTION_DOWN:
247 | downY = ev.getY();
248 | lastY = downY;
249 | timer.cancel();
250 | mEvents = 0;
251 | break;
252 | case MotionEvent.ACTION_POINTER_DOWN:
253 | case MotionEvent.ACTION_POINTER_UP:
254 | // 过滤多点触碰
255 | mEvents = -1;
256 | break;
257 | case MotionEvent.ACTION_MOVE:
258 | if (mEvents == 0)
259 | {
260 | if (((Pullable) pullableView).canPullDown())
261 | {
262 | // 可以下拉,正在加载时不能下拉
263 | // 对实际滑动距离做缩小,造成用力拉的感觉
264 | pullDownY = pullDownY + (ev.getY() - lastY) / radio;
265 | if (pullDownY < 0)
266 | {
267 | pullDownY = 0;
268 | }
269 | if (pullDownY > getMeasuredHeight())
270 | pullDownY = getMeasuredHeight();
271 | if (state == REFRESHING)
272 | {
273 | // 正在刷新的时候触摸移动
274 | isTouch = true;
275 | }
276 | }
277 | } else
278 | mEvents = 0;
279 | lastY = ev.getY();
280 | // 根据下拉距离改变比例
281 | radio = (float) (2 + 2 * Math.tan(Math.PI / 2 / getMeasuredHeight()
282 | * (pullDownY)));
283 | requestLayout();
284 | if (pullDownY <= refreshDist && state == RELEASE_TO_REFRESH)
285 | {
286 | // 如果下拉距离没达到刷新的距离且当前状态是释放刷新,改变状态为下拉刷新
287 | changeState(INIT);
288 | }
289 | if (pullDownY >= refreshDist && state == INIT)
290 | {
291 | // 如果下拉距离达到刷新的距离且当前状态是初始状态刷新,改变状态为释放刷新
292 | changeState(RELEASE_TO_REFRESH);
293 | }
294 | // 因为刷新和加载操作不能同时进行,所以pullDownY和pullUpY不会同时不为0,因此这里用(pullDownY +
295 | // Math.abs(pullUpY))就可以不对当前状态作区分了
296 | if ((pullDownY) > 8)
297 | {
298 | // 防止下拉过程中误触发长按事件和点击事件
299 | ev.setAction(MotionEvent.ACTION_CANCEL);
300 | }
301 | break;
302 | case MotionEvent.ACTION_UP:
303 | if (pullDownY > refreshDist)
304 | // 正在刷新时往下拉(正在加载时往上拉),释放后下拉头(上拉头)不隐藏
305 | isTouch = false;
306 | if (state == RELEASE_TO_REFRESH)
307 | {
308 | changeState(REFRESHING);
309 | // 刷新操作
310 | if (mListener != null)
311 | mListener.onRefresh(this);
312 | }
313 | hide();
314 | default:
315 | break;
316 | }
317 | // 事件分发交给父类
318 | super.dispatchTouchEvent(ev);
319 | return true;
320 | }
321 |
322 | private void initView()
323 | {
324 | // 初始化下拉布局
325 | pullView = refreshView.findViewById(R.id.pull_icon);
326 | refreshStateTextView = (TextView) refreshView
327 | .findViewById(R.id.state_tv);
328 | refreshingView = refreshView.findViewById(R.id.refreshing_icon);
329 | refreshStateImageView = refreshView.findViewById(R.id.state_iv);
330 | }
331 |
332 | @Override
333 | protected void onLayout(boolean changed, int l, int t, int r, int b)
334 | {
335 | if (!isLayout)
336 | {
337 | // 这里是第一次进来的时候做一些初始化
338 | refreshView = getChildAt(0);
339 | pullableView = getChildAt(1);
340 | isLayout = true;
341 | initView();
342 | refreshDist = ((ViewGroup) refreshView).getChildAt(0)
343 | .getMeasuredHeight();
344 | }
345 | // 改变子控件的布局,这里直接用(pullDownY + pullUpY)作为偏移量,这样就可以不对当前状态作区分
346 | refreshView.layout(0,
347 | (int) (pullDownY) - refreshView.getMeasuredHeight(),
348 | refreshView.getMeasuredWidth(), (int) (pullDownY));
349 | pullableView.layout(0, (int) (pullDownY),
350 | pullableView.getMeasuredWidth(), (int) (pullDownY)
351 | + pullableView.getMeasuredHeight());
352 | }
353 |
354 | class MyTimer
355 | {
356 | private Handler handler;
357 | private Timer timer;
358 | private MyTask mTask;
359 |
360 | public MyTimer(Handler handler)
361 | {
362 | this.handler = handler;
363 | timer = new Timer();
364 | }
365 |
366 | public void schedule(long period)
367 | {
368 | if (mTask != null)
369 | {
370 | mTask.cancel();
371 | mTask = null;
372 | }
373 | mTask = new MyTask(handler);
374 | timer.schedule(mTask, 0, period);
375 | }
376 |
377 | public void cancel()
378 | {
379 | if (mTask != null)
380 | {
381 | mTask.cancel();
382 | mTask = null;
383 | }
384 | }
385 |
386 | class MyTask extends TimerTask
387 | {
388 | private Handler handler;
389 |
390 | public MyTask(Handler handler)
391 | {
392 | this.handler = handler;
393 | }
394 |
395 | @Override
396 | public void run()
397 | {
398 | handler.obtainMessage().sendToTarget();
399 | }
400 |
401 | }
402 | }
403 |
404 | /**
405 | * 刷新加载回调接口
406 | *
407 | * @author chenjing
408 | *
409 | */
410 | public interface OnRefreshListener
411 | {
412 | /**
413 | * 刷新操作
414 | */
415 | void onRefresh(PullToRefreshLayout pullToRefreshLayout);
416 |
417 | }
418 |
419 | }
420 |
--------------------------------------------------------------------------------
/src/com/jingchen/autoload/Pullable.java:
--------------------------------------------------------------------------------
1 | package com.jingchen.autoload;
2 |
3 | public interface Pullable
4 | {
5 | /**
6 | * 判断是否可以下拉,如果不需要下拉功能可以直接return false
7 | *
8 | * @return true如果可以下拉否则返回false
9 | */
10 | boolean canPullDown();
11 | }
12 |
--------------------------------------------------------------------------------
/src/com/jingchen/autoload/PullableListView.java:
--------------------------------------------------------------------------------
1 | package com.jingchen.autoload;
2 |
3 | import android.content.Context;
4 | import android.graphics.drawable.AnimationDrawable;
5 | import android.util.AttributeSet;
6 | import android.view.LayoutInflater;
7 | import android.view.MotionEvent;
8 | import android.view.View;
9 | import android.widget.ImageView;
10 | import android.widget.ListView;
11 | import android.widget.TextView;
12 |
13 | /**
14 | * 如果不需要下拉刷新直接在canPullDown中返回false,这里的自动加载和下拉刷新没有冲突,通过增加在尾部的footerview实现自动加载,
15 | * 所以在使用中不要再动footerview了
16 | * 更多详解见博客http://blog.csdn.net/zhongkejingwang/article/details/38963177
17 | * @author chenjing
18 | *
19 | */
20 | public class PullableListView extends ListView implements Pullable
21 | {
22 | public static final int INIT = 0;
23 | public static final int LOADING = 1;
24 | private OnLoadListener mOnLoadListener;
25 | private View mLoadmoreView;
26 | private ImageView mLoadingView;
27 | private TextView mStateTextView;
28 | private int state = INIT;
29 | private boolean canLoad = true;
30 | private boolean autoLoad = true;
31 | private AnimationDrawable mLoadAnim;
32 |
33 | public PullableListView(Context context)
34 | {
35 | super(context);
36 | init(context);
37 | }
38 |
39 | public PullableListView(Context context, AttributeSet attrs)
40 | {
41 | super(context, attrs);
42 | init(context);
43 | }
44 |
45 | public PullableListView(Context context, AttributeSet attrs, int defStyle)
46 | {
47 | super(context, attrs, defStyle);
48 | init(context);
49 | }
50 |
51 | private void init(Context context)
52 | {
53 | mLoadmoreView = LayoutInflater.from(context).inflate(R.layout.load_more,
54 | null);
55 | mLoadingView = (ImageView) mLoadmoreView.findViewById(R.id.loading_icon);
56 | mLoadingView.setBackgroundResource(R.anim.loading_anim);
57 | mLoadAnim = (AnimationDrawable) mLoadingView.getBackground();
58 | mStateTextView = (TextView) mLoadmoreView.findViewById(R.id.loadstate_tv);
59 | mLoadmoreView.setOnClickListener(new OnClickListener()
60 | {
61 |
62 | @Override
63 | public void onClick(View v)
64 | {
65 | //点击加载
66 | if(state != LOADING){
67 | load();
68 | }
69 | }
70 | });
71 | addFooterView(mLoadmoreView, null, false);
72 | }
73 |
74 | /**
75 | * 是否开启自动加载
76 | * @param enable true启用,false禁用
77 | */
78 | public void enableAutoLoad(boolean enable){
79 | autoLoad = enable;
80 | }
81 |
82 | /**
83 | * 是否显示加载更多
84 | * @param v true显示,false不显示
85 | */
86 | public void setLoadmoreVisible(boolean v){
87 | if(v)
88 | {
89 | addFooterView(mLoadmoreView, null, false);
90 | }
91 | else {
92 | removeFooterView(mLoadmoreView);
93 | }
94 | }
95 |
96 | @Override
97 | public boolean onTouchEvent(MotionEvent ev)
98 | {
99 | switch (ev.getActionMasked())
100 | {
101 | case MotionEvent.ACTION_DOWN:
102 | // 按下的时候禁止自动加载
103 | canLoad = false;
104 | break;
105 | case MotionEvent.ACTION_UP:
106 | // 松开手判断是否自动加载
107 | canLoad = true;
108 | checkLoad();
109 | break;
110 | }
111 | return super.onTouchEvent(ev);
112 | }
113 |
114 | @Override
115 | protected void onScrollChanged(int l, int t, int oldl, int oldt)
116 | {
117 | super.onScrollChanged(l, t, oldl, oldt);
118 | // 在滚动中判断是否满足自动加载条件
119 | checkLoad();
120 | }
121 |
122 | /**
123 | * 判断是否满足自动加载条件
124 | */
125 | private void checkLoad()
126 | {
127 | if (reachBottom() && mOnLoadListener != null && state != LOADING
128 | && canLoad && autoLoad)
129 | load();
130 | }
131 |
132 | private void load(){
133 | changeState(LOADING);
134 | mOnLoadListener.onLoad(this);
135 | }
136 |
137 | private void changeState(int state)
138 | {
139 | this.state = state;
140 | switch (state)
141 | {
142 | case INIT:
143 | mLoadAnim.stop();
144 | mLoadingView.setVisibility(View.INVISIBLE);
145 | mStateTextView.setText(R.string.more);
146 | break;
147 |
148 | case LOADING:
149 | mLoadingView.setVisibility(View.VISIBLE);
150 | mLoadAnim.start();
151 | mStateTextView.setText(R.string.loading);
152 | break;
153 | }
154 | }
155 |
156 | /**
157 | * 完成加载
158 | */
159 | public void finishLoading()
160 | {
161 | changeState(INIT);
162 | }
163 |
164 | @Override
165 | public boolean canPullDown()
166 | {
167 | if (getCount() == 0)
168 | {
169 | // 没有item的时候也可以下拉刷新
170 | return true;
171 | } else if (getFirstVisiblePosition() == 0
172 | && getChildAt(0).getTop() >= 0)
173 | {
174 | // 滑到ListView的顶部了
175 | return true;
176 | } else
177 | return false;
178 | }
179 |
180 | public void setOnLoadListener(OnLoadListener listener)
181 | {
182 | this.mOnLoadListener = listener;
183 | }
184 |
185 | /**
186 | * @return footerview可见时返回true,否则返回false
187 | */
188 | private boolean reachBottom()
189 | {
190 | if (getCount() == 0)
191 | {
192 | return true;
193 | } else if (getLastVisiblePosition() == (getCount() - 1))
194 | {
195 | // 滑到底部,且頂部不是第0个,也就是说item数超过一屏后才能自动加载,否则只能点击加载
196 | if (getChildAt(getLastVisiblePosition() - getFirstVisiblePosition()) != null
197 | && getChildAt(
198 | getLastVisiblePosition()
199 | - getFirstVisiblePosition()).getTop() < getMeasuredHeight() && !canPullDown())
200 | return true;
201 | }
202 | return false;
203 | }
204 |
205 | public interface OnLoadListener
206 | {
207 | void onLoad(PullableListView pullableListView);
208 | }
209 | }
210 |
--------------------------------------------------------------------------------