1 import android.animation.Animator;
2 import android.animation.Animator.AnimatorListener;
3 import android.animation.TypeEvaluator;
4 import android.animation.ValueAnimator;
5 import android.animation.ValueAnimator.AnimatorUpdateListener;
6 import android.content.Context;
7 import android.util.AttributeSet;
8 import android.view.LayoutInflater;
9 import android.view.MotionEvent;
10 import android.view.View;
11 import android.view.ViewGroup;
12 import android.view.animation.DecelerateInterpolator;
13 import android.widget.ImageView;
14 import android.widget.LinearLayout;
15 import android.widget.ListView;
16 import android.widget.ProgressBar;
17
18 import com.customview.R;
19
20 public class DownRefreshListView
extends ListView {
21
22 public DownRefreshListView(Context context) {
23 super(context);
24 init();
25 }
26
27 public DownRefreshListView(Context context, AttributeSet attrs) {
28 super(context, attrs);
29 init();
30 }
31
32 public DownRefreshListView(Context context, AttributeSet attrs,
int defStyle) {
33 super(context, attrs, defStyle);
34 init();
35 }
36
37 private void init() {
38 // Listview的headerview(注意headerview的Layout布局要为LinearLayout)
39 mHeaderView =
(LinearLayout) LayoutInflater.from(getContext()).inflate(
40 R.layout.header_view,
null);
41 // 指示箭头
42 mArrowImage =
(ImageView) mHeaderView.findViewById(R.id.headerArrow);
43 // 指示进度
44 mProgressbar =
(ProgressBar) mHeaderView
45 .findViewById(R.id.headerProgressbar);
46 // 测量
47 measureView(mHeaderView);
48 // 获取测量后的headerview高度
49 mHeaderHeight =
mHeaderView.getMeasuredHeight();
50 // 设置headerview的顶部缩进
51 mHeaderView.setPadding(mHeaderView.getPaddingLeft(), -
mHeaderHeight,
52 mHeaderView.getPaddingRight(), mHeaderView.getPaddingBottom());
53 // 添加headerview
54 this.addHeaderView(mHeaderView,
null,
false);
55 this.setDivider(
null);
56 this.setVerticalScrollBarEnabled(
false);
57 }
58
59 private void measureView(View v) {
60 ViewGroup.LayoutParams p =
v.getLayoutParams();
61 if (p ==
null) {
62 p =
new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
63 ViewGroup.LayoutParams.WRAP_CONTENT);
64
65 }
66 int viewWidthSpec = ViewGroup.getChildMeasureSpec(0, 0
, p.width);
67 int viewHeightSpec = ViewGroup.getChildMeasureSpec(0, 0
, p.height);
68 v.measure(viewWidthSpec, viewHeightSpec);
69 }
70
71 @Override
72 public boolean onTouchEvent(MotionEvent ev) {
73 // 正在refresh data时,直接返回
74 if (mState ==
State.REFRESH_LOADING) {
75 return super.onTouchEvent(ev);
76 }
77 int action =
ev.getAction();
78 switch (action) {
79 case MotionEvent.ACTION_DOWN:
80 mLastY =
ev.getY();
81 // 当前可视位置为顶部
82 if (mState == State.IDLE && getFirstVisiblePosition() == 0
83 && !
hasStarted) {
84 mState =
State.PULL_TO_REFRESH;
85 hasStarted =
true;
86 return true;
87 }
88 // 允许动画播放时,用户点击,终止动画,同时继续下拉动作
89 if ((mState == State.PULL_TO_REFRESH || mState ==
State.ENOUGH_TO_REFRESH)
90 && hasStarted &&
isAnimated) {
91 isCancelManually =
true;
92 // 调用cancel方法,亦会调用animator listener的onAnimationEnd方法
93 mValueAnimator.cancel();
94 return true;
95 }
96 break;
97 case MotionEvent.ACTION_MOVE:
98 mCurrentY =
ev.getY();
99 float deltaY = mCurrentY -
mLastY;
100 mLastY =
mCurrentY;
101 // 已经开始,且滑动距离大于认定最小滑动距离
102 if (hasStarted && Math.abs(deltaY) >
mCustomScaledTouchSlop) {
103 mScrollStated =
true;
104 }
105 if (mScrollStated && mState !=
State.IDLE
106 && mState !=
State.REFRESH_LOADING) {
107 // 向下滑动
108 if (deltaY > 0
) {
109 // 设置headerview的顶部缩进
110 int paddingTop =
mHeaderView.getPaddingTop()
111 + (
int) (deltaY / 3
);
112 mHeaderView.setPadding(0, paddingTop, 0, 0
);
113 // headerview完全可见时,当前状态更动为释放刷新
114 if (paddingTop >= 0
) {
115 mState =
State.ENOUGH_TO_REFRESH;
116 }
else {
117 mState =
State.PULL_TO_REFRESH;
118 }
119 // 大于二分之一可见时,转动箭头
120 if (paddingTop > -mHeaderHeight * 0.5f
) {
121 float degree = (paddingTop + mHeaderHeight * 0.5f
)
122 / (mHeaderHeight * 0.5f) * 180
;
123 mArrowImage.setRotation(Math.min(degree, 180
));
124 }
125 }
else {
126 // 向上滑动
127 int paddingTop =
mHeaderView.getPaddingTop()
128 + (
int) (deltaY / 3
);
129 // 当前headerview完全可见,优先减少headerview缩进距离
130 if (paddingTop > 0
) {
131 mState =
State.ENOUGH_TO_REFRESH;
132 }
else if (paddingTop > -
mHeaderHeight) {
133 // 当前headerview不完全可见,箭头转动角度回滚
134 float degree = (paddingTop + mHeaderHeight * 0.5f
)
135 / (mHeaderHeight * 0.5f) * 180
;
136 mArrowImage.setRotation(Math.max(0
, degree));
137 // 当前headerview不完全可见,设置当前状态为下拉刷新状态
138 mState =
State.PULL_TO_REFRESH;
139 }
else {
140 // 当前headerview不可见
141 mHeaderView.setPadding(0, -mHeaderHeight, 0, 0
);
142 mState =
State.IDLE;
143 return super.onTouchEvent(ev);
144 }
145 mHeaderView.setPadding(0, paddingTop, 0, 0
);
146 }
147 return true;
148 }
149 break;
150 case MotionEvent.ACTION_UP:
151 if (mState ==
State.PULL_TO_REFRESH) {
152 // 下拉刷新状态下,手指弹起
153 smoothAnimate(300, mHeaderView.getPaddingTop(), -
mHeaderHeight);
154 if (mScrollStated) {
155 mScrollStated =
false;
156 return true;
157 }
158 }
else if (mState ==
State.ENOUGH_TO_REFRESH) {
159 // 释放刷新状态下,手指弹起
160 smoothAnimate(300, mHeaderView.getPaddingTop(), 0
);
161 return true;
162 }
else {
163 mState =
State.IDLE;
164 hasStarted =
false;
165 mScrollStated =
false;
166 }
167 break;
168 }
169 return super.onTouchEvent(ev);
170 }
171
172 /** 平滑动画,使headerview的顶部缩进回到指定状态. */
173 private void smoothAnimate(
int duration,
int startValue,
int endValue) {
174 mValueAnimator = ValueAnimator.ofObject(
new TypeEvaluator<Integer>
() {
175
176 @Override
177 public Integer evaluate(
float fraction, Integer startValue,
178 Integer endValue) {
179 return (
int) (startValue + fraction * (endValue -
startValue));
180 }
181 }, startValue, endValue);
182 mValueAnimator.addUpdateListener(
new AnimatorUpdateListener() {
183
184 @Override
185 public void onAnimationUpdate(ValueAnimator animation) {
186 int middleValue =
(Integer) animation.getAnimatedValue();
187 // headerview顶部缩进回滚
188 mHeaderView.setPadding(0, middleValue, 0, 0
);
189 // headerview指示箭头转动角度回滚
190 float degree = (mHeaderView.getPaddingTop() + 0.5f *
mHeaderHeight)
191 / (mHeaderHeight * 0.5f) * 180
;
192 if (degree > 180
) {
193 degree = 180
;
194 }
195 mArrowImage.setRotation(Math.max(0
, degree));
196 }
197 });
198 // 动画监听
199 mValueAnimator.addListener(
new AnimatorListener() {
200
201 @Override
202 public void onAnimationStart(Animator animation) {
203 isAnimated =
true;
204 }
205
206 @Override
207 public void onAnimationRepeat(Animator animation) {
208 }
209
210 @Override
211 public void onAnimationEnd(Animator animation) {
212 isAnimated =
false;
213 // 手动终止动画时,不改变当前状态,允许继续下拉
214 if (isCancelManually) {
215 isCancelManually =
false;
216 return;
217 }
218 if (isRefreshComplete) {
219 isRefreshComplete =
false;
220 mProgressbar.setVisibility(View.GONE);
221 mArrowImage.setVisibility(View.VISIBLE);
222 mArrowImage.setRotation(0
);
223 }
224 if (mState ==
State.PULL_TO_REFRESH) {
225 mState =
State.IDLE;
226 hasStarted =
false;
227 }
228 if (mState ==
State.ENOUGH_TO_REFRESH) {
229 mState =
State.REFRESH_LOADING;
230 mArrowImage.setVisibility(View.GONE);
231 mProgressbar.setVisibility(View.VISIBLE);
232 hasStarted =
false;
233 mScrollStated =
false;
234 if (mRefreshExecutor !=
null) {
235 mRefreshExecutor.refreshData();
236 }
237 }
238 }
239
240 @Override
241 public void onAnimationCancel(Animator animation) {
242 isAnimated =
false;
243 }
244 });
245 mValueAnimator.setDuration(duration);
246 mValueAnimator.setInterpolator(
new DecelerateInterpolator());
247 mValueAnimator.start();
248 }
249
250 /** 刷新完成后,由刷新任务执行者,调用刷新完成 */
251 public void refreshComplete() {
252 mState =
State.IDLE;
253 isRefreshComplete =
true;
254 smoothAnimate(500, mHeaderView.getPaddingTop(), -
mHeaderHeight);
255 }
256
257 public void setRefreshExecutor(RefreshExecutor executor) {
258 mRefreshExecutor =
executor;
259 }
260
261 /** 刷新数据回调接口. */
262 public interface RefreshExecutor {
263 public void refreshData();
264 }
265
266 public enum State {
267 /** 空闲状态. */
268 IDLE,
269 /** 下拉刷新状态. */
270 PULL_TO_REFRESH,
271 /** 释放可刷新状态. */
272 ENOUGH_TO_REFRESH,
273 /** 正在刷新状态. */
274 REFRESH_LOADING
275 }
276
277 private LinearLayout mHeaderView;
278 private ImageView mArrowImage;
279 private ProgressBar mProgressbar;
280 private int mHeaderHeight;
281 /** 当前状态. */
282 private State mState =
State.IDLE;
283 /** 是否开始下拉. */
284 private boolean hasStarted =
false;
285 /** 是否开始滚动. */
286 private boolean mScrollStated =
false;
287 /** 下拉刷新回调接口. */
288 private RefreshExecutor mRefreshExecutor;
289 /** 是否手动停止动画播放. */
290 private boolean isRefreshComplete =
false;
291
292 /** 属性动画. */
293 private ValueAnimator mValueAnimator;
294 /** 动画是否已经播放. */
295 private boolean isAnimated =
false;
296 /** 是否手动停止动画播放. */
297 private boolean isCancelManually =
false;
298
299 /** 自定义最小点击滚动距离. */
300 private final int mCustomScaledTouchSlop = 6
;
301 /** 上一次Y坐标. */
302 private float mLastY;
303 /** 当前Y坐标. */
304 private float mCurrentY;
305 }
View Code
header_view.xml
1 <?xml version="1.0" encoding="utf-8"?>
2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
3 android:layout_width="match_parent"
4 android:layout_height="wrap_content"
5 android:orientation="vertical" >
6
7 <ProgressBar
8 android:id="@+id/headerProgressbar"
9 android:layout_width="30dp"
10 android:layout_height="30dp"
11 android:layout_gravity="center"
12 android:layout_marginBottom="3dp"
13 android:layout_marginTop="3dp"
14 android:indeterminateBehavior="repeat"
15 android:indeterminateDrawable="@drawable/custom_progress_drawable"
16 android:indeterminateDuration="1000"
17 android:visibility="gone" />
18
19 <ImageView
20 android:id="@+id/headerArrow"
21 android:layout_width="30dp"
22 android:layout_height="30dp"
23 android:layout_gravity="center"
24 android:layout_marginBottom="3dp"
25 android:layout_marginTop="3dp"
26 android:contentDescription="@null"
27 android:src="@drawable/down_arrow" >
28 </ImageView>
29
30
31 </LinearLayout>
View Code
ProgressBar样式及背景
1 <?xml version="1.0" encoding="utf-8"?>
2 <rotate xmlns:android="http://schemas.android.com/apk/res/android"
3 android:drawable="@drawable/progress_refresh"
4 android:fromDegrees="0"
5 android:pivotX="50%"
6 android:pivotY="50%"
7 android:toDegrees="360" >
8 </rotate>
View Code
用到的资源:
down_arrow.png
progress_refresh.png
转载于:https://www.cnblogs.com/Professionalbutcher/p/3780340.html
相关资源:垃圾分类数据集及代码