Android中重写ListView实现下拉刷新

最近项目中刚好有下拉刷新的需求,于是找了一些开源的资料研究了一下,看到网上有很多实现方式,有的继承Linearlayout,有的重写ScrollView,还有的重写Gridview,看到最多的还是重写listview,这里采用哪一种方式的实现没有对错之分,关键要看需求的设计。

这里还是采用大家用的最多的listview实现下拉刷新的操作。

效果如下图:

device-2013-09-20-203440

device-2013-09-20-203530

下面我们来看下代码:

其主要代码是listview实现:

001 public class PullToRefreshListView extends ListView implements OnScrollListener {
002
003 private final static String TAG = "PullToRefreshListView";
004
005 // 下拉刷新标志
006 private final static int PULL_To_REFRESH = 0;
007 // 松开刷新标志
008 private final static int RELEASE_To_REFRESH = 1;
009 // 正在刷新标志
010 private final static int REFRESHING = 2;
011 // 刷新完成标志
012 private final static int DONE = 3;
013
014 private LayoutInflater inflater;
015
016 private LinearLayout headView;
017 private TextView tipsTextview;
018 private TextView lastUpdatedTextView;
019 private ImageView arrowImageView;
020 private ProgressBar progressBar;
021 // 用来设置箭头图标动画效果
022 private RotateAnimation animation;
023 private RotateAnimation reverseAnimation;
024
025 // 用于保证startY的值在一个完整的touch事件中只被记录一次
026 private boolean isRecored;
027
028 private int headContentWidth;
029 private int headContentHeight;
030 private int headContentOriginalTopPadding;
031
032 private int startY;
033 private int firstItemIndex;
034 private int currentScrollState;
035
036 private int state;
037
038 private boolean isBack;
039
040 public OnRefreshListener refreshListener;
041
042 public PullToRefreshListView(Context context, AttributeSet attrs) {
043 super(context, attrs);
044 init(context);
045 }
046
047 public PullToRefreshListView(Context context, AttributeSet attrs, int defStyle) {
048 super(context, attrs, defStyle);
049 init(context);
050 }
051
052 private void init(Context context) {
053 //设置滑动效果
054 animation = new RotateAnimation(0, -180,
055 RotateAnimation.RELATIVE_TO_SELF, 0.5f,
056 RotateAnimation.RELATIVE_TO_SELF, 0.5f);
057 animation.setInterpolator(new LinearInterpolator());
058 animation.setDuration(100);
059 animation.setFillAfter(true);
060
061 reverseAnimation = new RotateAnimation(-180, 0,
062 RotateAnimation.RELATIVE_TO_SELF, 0.5f,
063 RotateAnimation.RELATIVE_TO_SELF, 0.5f);
064 reverseAnimation.setInterpolator(new LinearInterpolator());
065 reverseAnimation.setDuration(100);
066 reverseAnimation.setFillAfter(true);
067
068 inflater = LayoutInflater.from(context);
069 headView = (LinearLayout) inflater.inflate(R.layout.pull_to_refresh_head, null);
070
071 arrowImageView = (ImageView) headView.findViewById(R.id.head_arrowImageView);
072 arrowImageView.setMinimumWidth(50);
073 arrowImageView.setMinimumHeight(50);
074 progressBar = (ProgressBar) headView.findViewById(R.id.head_progressBar);
075 tipsTextview = (TextView) headView.findViewById(R.id.head_tipsTextView);
076 lastUpdatedTextView = (TextView) headView.findViewById(R.id.head_lastUpdatedTextView);
077
078 headContentOriginalTopPadding = headView.getPaddingTop();
079
080 measureView(headView);
081 headContentHeight = headView.getMeasuredHeight();
082 headContentWidth = headView.getMeasuredWidth();
083
084 headView.setPadding(headView.getPaddingLeft(), -1 * headContentHeight, headView.getPaddingRight(), headView.getPaddingBottom());
085 headView.invalidate();
086
087 //System.out.println("初始高度:"+headContentHeight);
088 //System.out.println("初始TopPad:"+headContentOriginalTopPadding);
089
090 addHeaderView(headView);
091 setOnScrollListener(this);
092 }
093
094 public void onScroll(AbsListView view, int firstVisiableItem, int visibleItemCount, int totalItemCount) {
095 firstItemIndex = firstVisiableItem;
096 }
097
098 public void onScrollStateChanged(AbsListView view, int scrollState) {
099 currentScrollState = scrollState;
100 }
101
102 public boolean onTouchEvent(MotionEvent event) {
103 switch (event.getAction()) {
104 case MotionEvent.ACTION_DOWN:
105 if (firstItemIndex == 0 && !isRecored) {
106 startY = (int) event.getY();
107 isRecored = true;
108 //System.out.println("当前-按下高度-ACTION_DOWN-Y:"+startY);
109 }
110 break;
111
112 case MotionEvent.ACTION_CANCEL://失去焦点&取消动作
113 case MotionEvent.ACTION_UP:
114
115 if (state != REFRESHING) {
116 if (state == DONE) {
117 //System.out.println("当前-抬起-ACTION_UP:DONE什么都不做");
118 }
119 else if (state == PULL_To_REFRESH) {
120 state = DONE;
121 changeHeaderViewByState();
122 //System.out.println("当前-抬起-ACTION_UP:PULL_To_REFRESH-->DONE-由下拉刷新状态到刷新完成状态");
123 }
124 else if (state == RELEASE_To_REFRESH) {
125 state = REFRESHING;
126 changeHeaderViewByState();
127 onRefresh();
128 //System.out.println("当前-抬起-ACTION_UP:RELEASE_To_REFRESH-->REFRESHING-由松开刷新状态,到刷新完成状态");
129 }
130 }
131
132 isRecored = false;
133 isBack = false;
134
135 break;
136
137 case MotionEvent.ACTION_MOVE:
138 int tempY = (int) event.getY();
139 //System.out.println("当前-滑动-ACTION_MOVE Y:"+tempY);
140 if (!isRecored && firstItemIndex == 0) {
141 //System.out.println("当前-滑动-记录拖拽时的位置 Y:"+tempY);
142 isRecored = true;
143 startY = tempY;
144 }
145 if (state != REFRESHING && isRecored) {
146 // 可以松开刷新了
147 if (state == RELEASE_To_REFRESH) {
148 // 往上推,推到屏幕足够掩盖head的程度,但还没有全部掩盖
149 if ((tempY - startY < headContentHeight+20)
150 && (tempY - startY) > 0) {
151 state = PULL_To_REFRESH;
152 changeHeaderViewByState();
153 //System.out.println("当前-滑动-ACTION_MOVE:RELEASE_To_REFRESH--》PULL_To_REFRESH-由松开刷新状态转变到下拉刷新状态");
154 }
155 // 一下子推到顶
156 else if (tempY - startY <= 0) {
157 state = DONE;
158 changeHeaderViewByState();
159 //System.out.println("当前-滑动-ACTION_MOVE:RELEASE_To_REFRESH--》DONE-由松开刷新状态转变到done状态");
160 }
161 // 往下拉,或者还没有上推到屏幕顶部掩盖head
162 else {
163 // 不用进行特别的操作,只用更新paddingTop的值就行了
164 }
165 }
166 // 还没有到达显示松开刷新的时候,DONE或者是PULL_To_REFRESH状态
167 else if (state == PULL_To_REFRESH) {
168 // 下拉到可以进入RELEASE_TO_REFRESH的状态
169 if (tempY - startY >= headContentHeight+20 && currentScrollState == SCROLL_STATE_TOUCH_SCROLL) {
170 state = RELEASE_To_REFRESH;
171 isBack = true;
172 changeHeaderViewByState();
173 //System.out.println("当前-滑动-PULL_To_REFRESH--》RELEASE_To_REFRESH-由done或者下拉刷新状态转变到松开刷新");
174 }
175 // 上推到顶了
176 else if (tempY - startY <= 0) {
177 state = DONE;
178 changeHeaderViewByState();
179 //System.out.println("当前-滑动-PULL_To_REFRESH--》DONE-由Done或者下拉刷新状态转变到done状态");
180 }
181 }
182 // done状态下
183 else if (state == DONE) {
184 if (tempY - startY > 0) {
185 state = PULL_To_REFRESH;
186 changeHeaderViewByState();
187 //System.out.println("当前-滑动-DONE--》PULL_To_REFRESH-由done状态转变到下拉刷新状态");
188 }
189 }
190
191 // 更新headView的size
192 if (state == PULL_To_REFRESH) {
193 int topPadding = (int)((-1 * headContentHeight + (tempY - startY)));
194 headView.setPadding(headView.getPaddingLeft(), topPadding, headView.getPaddingRight(), headView.getPaddingBottom());
195 headView.invalidate();
196 //System.out.println("当前-下拉刷新PULL_To_REFRESH-TopPad:"+topPadding);
197 }
198
199 // 更新headView的paddingTop
200 if (state == RELEASE_To_REFRESH) {
201 int topPadding = (int)((tempY - startY - headContentHeight));
202 headView.setPadding(headView.getPaddingLeft(), topPadding, headView.getPaddingRight(), headView.getPaddingBottom());
203 headView.invalidate();
204 //System.out.println("当前-释放刷新RELEASE_To_REFRESH-TopPad:"+topPadding);
205 }
206 }
207 break;
208 }
209 return super.onTouchEvent(event);
210 }
211
212 // 当状态改变时候,调用该方法,以更新界面
213 private void changeHeaderViewByState() {
214 switch (state) {
215 case RELEASE_To_REFRESH:
216
217 arrowImageView.setVisibility(View.VISIBLE);
218 progressBar.setVisibility(View.GONE);
219 tipsTextview.setVisibility(View.VISIBLE);
220 lastUpdatedTextView.setVisibility(View.VISIBLE);
221
222 arrowImageView.clearAnimation();
223 arrowImageView.startAnimation(animation);
224
225 tipsTextview.setText(R.string.pull_to_refresh_release_label);
226
227 //Log.v(TAG, "当前状态,松开刷新");
228 break;
229 case PULL_To_REFRESH:
230
231 progressBar.setVisibility(View.GONE);
232 tipsTextview.setVisibility(View.VISIBLE);
233 lastUpdatedTextView.setVisibility(View.VISIBLE);
234 arrowImageView.clearAnimation();
235 arrowImageView.setVisibility(View.VISIBLE);
236 if (isBack) {
237 isBack = false;
238 arrowImageView.clearAnimation();
239 arrowImageView.startAnimation(reverseAnimation);
240 }
241 tipsTextview.setText(R.string.pull_to_refresh_pull_label);
242
243 //Log.v(TAG, "当前状态,下拉刷新");
244 break;
245
246 case REFRESHING:
247 //System.out.println("刷新REFRESHING-TopPad:"+headContentOriginalTopPadding);
248 headView.setPadding(headView.getPaddingLeft(), headContentOriginalTopPadding, headView.getPaddingRight(), headView.getPaddingBottom());
249 headView.invalidate();
250
251 progressBar.setVisibility(View.VISIBLE);
252 arrowImageView.clearAnimation();
253 arrowImageView.setVisibility(View.GONE);
254 tipsTextview.setText(R.string.pull_to_refresh_refreshing_label);
255 lastUpdatedTextView.setVisibility(View.GONE);
256
257 //Log.v(TAG, "当前状态,正在刷新...");
258 break;
259 case DONE:
260 //System.out.println("完成DONE-TopPad:"+(-1 * headContentHeight));
261 headView.setPadding(headView.getPaddingLeft(), -1 * headContentHeight, headView.getPaddingRight(), headView.getPaddingBottom());
262 headView.invalidate();
263
264 progressBar.setVisibility(View.GONE);
265 arrowImageView.clearAnimation();
266 // 此处更换图标
267 arrowImageView.setImageResource(R.drawable.ic_pulltorefresh_arrow);
268
269 tipsTextview.setText(R.string.pull_to_refresh_pull_label);
270 lastUpdatedTextView.setVisibility(View.VISIBLE);
271
272 //Log.v(TAG, "当前状态,done");
273 break;
274 }
275 }
276
277 //点击刷新
278 public void clickRefresh() {
279 setSelection(0);
280 state = REFRESHING;
281 changeHeaderViewByState();
282 onRefresh();
283 }
284
285 public void setOnRefreshListener(OnRefreshListener refreshListener) {
286 this.refreshListener = refreshListener;
287 }
288
289 public interface OnRefreshListener {
290 public void onRefresh();
291 }
292
293 public void onRefreshComplete(String update) {
294 lastUpdatedTextView.setText(update);
295 onRefreshComplete();
296 }
297
298 public void onRefreshComplete() {
299 state = DONE;
300 changeHeaderViewByState();
301 }
302
303 private void onRefresh() {
304 if (refreshListener != null) {
305 refreshListener.onRefresh();
306 }
307 }
308
309 // 计算headView的width及height值
310 private void measureView(View child) {
311 ViewGroup.LayoutParams p = child.getLayoutParams();
312 if (p == null) {
313 p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
314 ViewGroup.LayoutParams.WRAP_CONTENT);
315 }
316 int childWidthSpec = ViewGroup.getChildMeasureSpec(0, 0 + 0, p.width);
317 int lpHeight = p.height;
318 int childHeightSpec;
319 if (lpHeight > 0) {
320 childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight,
321 MeasureSpec.EXACTLY);
322 } else {
323 childHeightSpec = MeasureSpec.makeMeasureSpec(0,
324 MeasureSpec.UNSPECIFIED);
325 }
326 child.measure(childWidthSpec, childHeightSpec);
327 }
328
329 }

里面的注释可以说写的相当的详细,我这里就不详细解释了,大家可以直接复制到项目中去看,下面我们来看看,如何使用它:

01 pullrefresh_lv = (PullToRefreshListView) findViewById(R.id.pullrefresh_lv);
02 testArr = new LinkedList<String>();
03 testArr.add("↓↓请下拉我看效果↓↓");
04 testAdapter = new TestAdapter(testArr, this);
05 pullrefresh_lv.setAdapter(testAdapter);
06
07 pullrefresh_lv
08 .setOnRefreshListener(new PullToRefreshListView.OnRefreshListener() {
09 public void onRefresh() {
10 // 这里进行网络操作/数据库操作/文件操作
11 handler.sendEmptyMessageDelayed(0, 2000);
12 }
13 });

使用起来也非常方便,和原生态listview无太多差别,只是多了一个 setOnRefreshListener方法,对的,这里就是处理刷新动作的地方,我这里没有进行网络操作,只是做了一个假数据演示(延迟刷新),下面来看看Handler里面做的处理:

01 Handler handler = new Handler() {
02 public void handleMessage(android.os.Message msg) {
03 switch (msg.what) {
04 case 0:
05 i++;
06 testArr.addFirst("刷新第" + i + "条");
07 pullrefresh_lv
08 .onRefreshComplete(getString(R.string.pull_to_refresh_update)
09 + new Date().toLocaleString());
10 pullrefresh_lv.onRefreshComplete();
11 testAdapter.notifyDataSetChanged();
12 break;
13
14 default:
15 break;
16 }
17 };
18 };

这里当我们刷新动作完成后,调用onRefreshComplete()结束该动作,到这里,整个下来刷新的操作流程就讲完了。

因本站存储空间有限,稍后将源码上传其它社区,有问题请留言,感谢大家。
代码已上传,附上链接:http://www.eoeandroid.com/forum.php?mod=viewthread&tid=304996&page=1&extra=#pid3147702

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值