Android 下拉刷新,上拉加载更多控件--支持ListView,GridView和ScrollView

Android 下拉刷新,上拉加载更多控件--支持ListView,GridView和ScrollView

由于这个文章比较长点,这里简单贴点代码,详细说明还是看原文吧 

 

 

主要源码

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
package  com.miloisbadboy.view;
 
import  android.content.Context;
import  android.util.AttributeSet;
import  android.util.DisplayMetrics;
import  android.util.Log;
import  android.view.LayoutInflater;
import  android.view.MotionEvent;
import  android.view.View;
import  android.view.ViewGroup;
import  android.view.animation.LinearInterpolator;
import  android.view.animation.RotateAnimation;
import  android.widget.AdapterView;
import  android.widget.ImageView;
import  android.widget.LinearLayout;
import  android.widget.ProgressBar;
import  android.widget.ScrollView;
import  android.widget.TextView;
 
import  com.miloisbadboy.R;
 
public  class  PullToRefreshView extends  LinearLayout {
     private  static  final  String TAG = "PullToRefreshView" ;
     // refresh states
     private  static  final  int  PULL_TO_REFRESH = 2 ;
     private  static  final  int  RELEASE_TO_REFRESH = 3 ;
     private  static  final  int  REFRESHING = 4 ;
     // pull state
     private  static  final  int  PULL_UP_STATE = 0 ;
     private  static  final  int  PULL_DOWN_STATE = 1 ;
     /**
      * last y
      */
     private  int  mLastMotionY;
     /**
      * lock
      */
     private  boolean  mLock;
     /**
      * header view
      */
     private  View mHeaderView;
     /**
      * footer view
      */
     private  View mFooterView;
     /**
      * list or grid
      */
     private  AdapterView<?> mAdapterView;
     /**
      * scrollview
      */
     private  ScrollView mScrollView;
     /**
      * header view height
      */
     private  int  mHeaderViewHeight;
     /**
      * footer view height
      */
     private  int  mFooterViewHeight;
     /**
      * header view image
      */
     private  ImageView mHeaderImageView;
     /**
      * footer view image
      */
     private  ImageView mFooterImageView;
     /**
      * header tip text
      */
     private  TextView mHeaderTextView;
     /**
      * footer tip text
      */
     private  TextView mFooterTextView;
     /**
      * header refresh time
      */
     private  TextView mHeaderUpdateTextView;
     /**
      * footer refresh time
      */
     // private TextView mFooterUpdateTextView;
     /**
      * header progress bar
      */
     private  ProgressBar mHeaderProgressBar;
     /**
      * footer progress bar
      */
     private  ProgressBar mFooterProgressBar;
     /**
      * layout inflater
      */
     private  LayoutInflater mInflater;
     /**
      * header view current state
      */
     private  int  mHeaderState;
     /**
      * footer view current state
      */
     private  int  mFooterState;
     /**
      * pull state,pull up or pull down;PULL_UP_STATE or PULL_DOWN_STATE
      */
     private  int  mPullState;
     /**
      * 变为向下的箭头,改变箭头方向
      */
     private  RotateAnimation mFlipAnimation;
     /**
      * 变为逆向的箭头,旋转
      */
     private  RotateAnimation mReverseFlipAnimation;
     /**
      * footer refresh listener
      */
     private  OnFooterRefreshListener mOnFooterRefreshListener;
     /**
      * footer refresh listener
      */
     private  OnHeaderRefreshListener mOnHeaderRefreshListener;
     /**
      * last update time
      */
     private  String mLastUpdateTime;
     public  PullToRefreshView(Context context, AttributeSet attrs) {
         super (context, attrs);
         init();
     }
 
     public  PullToRefreshView(Context context) {
         super (context);
         init();
     }
 
     /**
      * init
      *
      * @description
      * @param context
      */
     private  void  init() {
         // Load all of the animations we need in code rather than through XML
         mFlipAnimation = new  RotateAnimation( 0 , - 180 , RotateAnimation.RELATIVE_TO_SELF, 0 .5f,
                 RotateAnimation.RELATIVE_TO_SELF, 0 .5f);
         mFlipAnimation.setInterpolator( new  LinearInterpolator());
         mFlipAnimation.setDuration( 250 );
         mFlipAnimation.setFillAfter( true );
         mReverseFlipAnimation = new  RotateAnimation(- 180 , 0 , RotateAnimation.RELATIVE_TO_SELF,
                 0 .5f, RotateAnimation.RELATIVE_TO_SELF, 0 .5f);
         mReverseFlipAnimation.setInterpolator( new  LinearInterpolator());
         mReverseFlipAnimation.setDuration( 250 );
         mReverseFlipAnimation.setFillAfter( true );
 
         mInflater = LayoutInflater.from(getContext());
         // header view 在此添加,保证是第一个添加到linearlayout的最上端
         addHeaderView();
     }
 
     private  void  addHeaderView() {
         // header view
         mHeaderView = mInflater.inflate(R.layout.refresh_header, this , false );
 
         mHeaderImageView = (ImageView) mHeaderView.findViewById(R.id.pull_to_refresh_image);
         mHeaderTextView = (TextView) mHeaderView.findViewById(R.id.pull_to_refresh_text);
         mHeaderUpdateTextView = (TextView) mHeaderView
                 .findViewById(R.id.pull_to_refresh_updated_at);
         mHeaderProgressBar = (ProgressBar) mHeaderView.findViewById(R.id.pull_to_refresh_progress);
         // header layout
         measureView(mHeaderView);
         mHeaderViewHeight = mHeaderView.getMeasuredHeight();
         LayoutParams params = new  LayoutParams(LayoutParams.MATCH_PARENT, mHeaderViewHeight);
         // 设置topMargin的值为负的header View高度,即将其隐藏在最上方
         params.topMargin = -(mHeaderViewHeight);
         // mHeaderView.setLayoutParams(params1);
         addView(mHeaderView, params);
 
     }
 
     private  void  addFooterView() {
         // footer view
         mFooterView = mInflater.inflate(R.layout.refresh_footer, this , false );
         mFooterImageView = (ImageView) mFooterView.findViewById(R.id.pull_to_load_image);
         mFooterTextView = (TextView) mFooterView.findViewById(R.id.pull_to_load_text);
         mFooterProgressBar = (ProgressBar) mFooterView.findViewById(R.id.pull_to_load_progress);
         // footer layout
         measureView(mFooterView);
         mFooterViewHeight = mFooterView.getMeasuredHeight();
         LayoutParams params = new  LayoutParams(LayoutParams.MATCH_PARENT, mFooterViewHeight);
         //int top = getHeight();
         //params.topMargin =getHeight();//在这里getHeight()==0,但在onInterceptTouchEvent()方法里getHeight()已经有值了,不再是0;
         // getHeight()什么时候会赋值,稍候再研究一下
         // 由于是线性布局可以直接添加,只要AdapterView的高度是MATCH_PARENT,那么footer view就会被添加到最后,并隐藏
         addView(mFooterView, params);
     }
 
     @Override
     protected  void  onFinishInflate() {
         super .onFinishInflate();
         // footer view 在此添加保证添加到linearlayout中的最后
         addFooterView();
         initContentAdapterView();
     }
 
     /**
      * init AdapterView like ListView,GridView and so on;or init ScrollView
      *
      */
     private  void  initContentAdapterView() {
         int  count = getChildCount();
         if  (count < 3 ) {
             throw  new  IllegalArgumentException(
                     "this layout must contain 3 child views,and AdapterView or ScrollView must in the second position!" );
         }
         View view = null ;
         for ( int  i= 0 ;i<count- 1 ;++i){
             view = getChildAt(i);
             if  (view instanceof  AdapterView<?>) {
                 mAdapterView = (AdapterView<?>) view;
             }
             if  (view instanceof  ScrollView) {
                 // finish later
                 mScrollView = (ScrollView)view;
             }
         }
         if  (mAdapterView == null  && mScrollView== null ) {
             throw  new  IllegalArgumentException( "must contain a AdapterView or ScrollView in this layout!" );
         }
     }
     
     private  void  measureView(View child) {
         ViewGroup.LayoutParams p = child.getLayoutParams();
         if  (p == null ) {
             p = new  ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
                     ViewGroup.LayoutParams.WRAP_CONTENT);
         }
 
         int  childWidthSpec = ViewGroup.getChildMeasureSpec( 0 , 0  + 0 , p.width);
         int  lpHeight = p.height;
         int  childHeightSpec;
         if  (lpHeight > 0 ) {
             childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.EXACTLY);
         } else  {
             childHeightSpec = MeasureSpec.makeMeasureSpec( 0 , MeasureSpec.UNSPECIFIED);
         }
         child.measure(childWidthSpec, childHeightSpec);
     }
 
     @Override
     public  boolean  onInterceptTouchEvent(MotionEvent e) {
         int  y = ( int ) e.getRawY();
         switch  (e.getAction()) {
         case  MotionEvent.ACTION_DOWN:
             // 首先拦截down事件,记录y坐标
             mLastMotionY = y;
             break ;
         case  MotionEvent.ACTION_MOVE:
             // deltaY > 0 是向下运动,< 0是向上运动
             int  deltaY = y - mLastMotionY;
             if  (isRefreshViewScroll(deltaY)) {
                 return  true ;
             }
             break ;
         case  MotionEvent.ACTION_UP:
         case  MotionEvent.ACTION_CANCEL:
             break ;
         }
         return  false ;
     }
 
     /*
      * 如果在onInterceptTouchEvent()方法中没有拦截(即onInterceptTouchEvent()方法中 return
      * false)则由PullToRefreshView 的子View来处理;否则由下面的方法来处理(即由PullToRefreshView自己来处理)
      */
     @Override
     public boolean onTouchEvent(MotionEvent event) {
         if (mLock) {
             return true;
         }
         int y = (int) event.getRawY();
         switch (event.getAction()) {
         case MotionEvent.ACTION_DOWN:
             // onInterceptTouchEvent已经记录
             // mLastMotionY = y;
             break;
         case MotionEvent.ACTION_MOVE:
             int deltaY = y - mLastMotionY;
             if (mPullState == PULL_DOWN_STATE) {
                 // PullToRefreshView执行下拉
                 Log.i(TAG, " pull down!parent view move!");
                 headerPrepareToRefresh(deltaY);
                 // setHeaderPadding(-mHeaderViewHeight);
             } else if (mPullState == PULL_UP_STATE) {
                 // PullToRefreshView执行上拉
                 Log.i(TAG, "pull up!parent view move!");
                 footerPrepareToRefresh(deltaY);
             }
             mLastMotionY = y;
             break;
         case MotionEvent.ACTION_UP:
         case MotionEvent.ACTION_CANCEL:
             int topMargin = getHeaderTopMargin();
             if (mPullState == PULL_DOWN_STATE) {
                 if (topMargin >= 0) {
                     // 开始刷新
                     headerRefreshing();
                 } else {
                     // 还没有执行刷新,重新隐藏
                     setHeaderTopMargin(-mHeaderViewHeight);
                 }
             } else if (mPullState == PULL_UP_STATE) {
                 if(Math.abs(topMargin)>=mHeaderViewHeight+mFooterViewHeight){
                     // 开始执行footer 刷新
                     footerRefreshing();
                 } else {
                     // 还没有执行刷新,重新隐藏
                     setHeaderTopMargin(-mHeaderViewHeight);
                 }
             }
             break;
         }
         return super.onTouchEvent(event);
     }
 
     /**
      * 是否应该到了父View,即PullToRefreshView滑动
      *
      * @param deltaY
      *            , deltaY > 0 是向下运动,< 0是向上运动
      * @return
      */
     private boolean isRefreshViewScroll(int deltaY) {
         if (mHeaderState == REFRESHING || mFooterState == REFRESHING) {
             return false;
         }
         //对于ListView 以及GridView
         if (mAdapterView != null) {
             View child = mAdapterView.getChildAt(0);
             if (child == null) {
                 // 如果mAdapterView中没有数据,不拦截
                 // 可以考虑返回true
                 return false;
             }
             // 子view(ListView or GridView)滑动到最顶端
             if (deltaY > 0 && child.getTop() == 0) {
                 mPullState = PULL_DOWN_STATE;
                 return true;
             } else if (deltaY < 0) {
                 View lastChild = mAdapterView.getChildAt(mAdapterView.getChildCount() - 1);
                 if (lastChild == null) {
                     // 如果mAdapterView中没有数据,不拦截
                     return false;
                 }
                 // 最后一个子view的Bottom小于父View的高度说明mAdapterView的数据没有填满父view,
                 // 等于父View的高度说明mAdapterView已经滑动到最后
                 if (lastChild.getBottom() <= getHeight()) {
                     mPullState = PULL_UP_STATE;
                     return true;
                 }
             }
         }
         //对于ScrollView
         if(mScrollView!=null){
             // 子scroll view滑动到最顶端
             View child = mScrollView.getChildAt(0);
             if (deltaY > 0 && mScrollView.getScrollY() == 0) {
                 mPullState = PULL_DOWN_STATE;
                 return true;
             } else if (deltaY < 0 && child.getMeasuredHeight()<=getHeight()+mScrollView.getScrollY()) {
                 mPullState = PULL_UP_STATE;
                 return true;
             }
         }
         return false;
     }
 
     /**
      * header 准备刷新,手指移动过程,还没有释放
      *
      * @param deltaY
      *            ,手指滑动的距离
      */
     private void headerPrepareToRefresh(int deltaY) {
         int newTopMargin = changingHeaderViewTopMargin(deltaY);
         // 当header view的topMargin>=0时,说明已经完全显示出来了,修改header view 的提示状态
         if (newTopMargin >= 0 && mHeaderState != RELEASE_TO_REFRESH) {
             mHeaderTextView.setText(R.string.pull_to_refresh_release_label);
             mHeaderUpdateTextView.setVisibility(View.VISIBLE);
             mHeaderImageView.clearAnimation();
             mHeaderImageView.startAnimation(mFlipAnimation);
             mHeaderState = RELEASE_TO_REFRESH;
         }
     }
 
     /**
      * footer 准备刷新,手指移动过程,还没有释放 移动footer view高度同样和移动header view
      * 高度是一样,都是通过修改header view的topmargin的值来达到
      *
      * @param deltaY
      *            ,手指滑动的距离
      */
     private void footerPrepareToRefresh(int deltaY) {
         int newTopMargin = changingHeaderViewTopMargin(deltaY);
         //如果header view topMargin 的绝对值大于或等于header + footer 的高度
         //说明footer view 完全显示出来了,修改footer view 的提示状态
         if(Math.abs(newTopMargin)>=(mHeaderViewHeight+mFooterViewHeight)
                 && mFooterState != RELEASE_TO_REFRESH){
             mFooterTextView.setText(R.string.pull_to_refresh_footer_release_label);
             mFooterImageView.clearAnimation();
             mFooterImageView.startAnimation(mFlipAnimation);
             mFooterState = RELEASE_TO_REFRESH;
         }
     }
     /**
      * 修改Header view top margin的值
      * @description    
      * @param deltaY
      * @return
      */
     private int changingHeaderViewTopMargin(int deltaY){
         LayoutParams params = (LayoutParams) mHeaderView.getLayoutParams();
         float newTopMargin = params.topMargin + deltaY * 0.3f;
         params.topMargin = (int) newTopMargin;
         mHeaderView.setLayoutParams(params);
         invalidate();
         return params.topMargin;
     }
     /**
      * header refreshing
      *
      */
     private void headerRefreshing() {
         mHeaderState = REFRESHING;
         setHeaderTopMargin(0);
         mHeaderImageView.setVisibility(View.GONE);
         mHeaderImageView.clearAnimation();
         mHeaderImageView.setImageDrawable(null);
         mHeaderProgressBar.setVisibility(View.VISIBLE);
         mHeaderTextView.setText(R.string.pull_to_refresh_refreshing_label);
         if (mOnHeaderRefreshListener != null) {
             mOnHeaderRefreshListener.onHeaderRefresh(this);
         }
     }
 
     /**
      * footer refreshing
      *
      */
     private void footerRefreshing() {
         mFooterState = REFRESHING;
         int top = mHeaderViewHeight + mFooterViewHeight;
         setHeaderTopMargin(-top);
         mFooterImageView.setVisibility(View.GONE);
         mFooterImageView.clearAnimation();
         mFooterImageView.setImageDrawable(null);
         mFooterProgressBar.setVisibility(View.VISIBLE);
         mFooterTextView.setText(R.string.pull_to_refresh_footer_refreshing_label);
         if (mOnFooterRefreshListener != null) {
             mOnFooterRefreshListener.onFooterRefresh(this);
         }
     }
 
     /**
      * 设置header view 的topMargin的值
      *
      * @description
      * @param topMargin
      *            ,为0时,说明header view 刚好完全显示出来; 为-mHeaderViewHeight时,说明完全隐藏了
      */
     private void setHeaderTopMargin(int topMargin) {
         LayoutParams params = (LayoutParams) mHeaderView.getLayoutParams();
         params.topMargin = topMargin;
         mHeaderView.setLayoutParams(params);
         invalidate();
     }
 
     /**
      * header view 完成更新后恢复初始状态
      *
      */
     public void onHeaderRefreshComplete() {
         setHeaderTopMargin(-mHeaderViewHeight);
         mHeaderImageView.setVisibility(View.VISIBLE);
         mHeaderImageView.setImageResource(R.drawable.ic_pulltorefresh_arrow);
         mHeaderTextView.setText(R.string.pull_to_refresh_pull_label);
         mHeaderProgressBar.setVisibility(View.GONE);
         // mHeaderUpdateTextView.setText("");
         mHeaderState = PULL_TO_REFRESH;
     }
     /**
      * Resets the list to a normal state after a refresh.
      * @param lastUpdated Last updated at.
      */
     public void onHeaderRefreshComplete(CharSequence lastUpdated) {
         setLastUpdated(lastUpdated);
         onHeaderRefreshComplete();
     }
     /**
      * footer view 完成更新后恢复初始状态
      */
     public void onFooterRefreshComplete() {
         setHeaderTopMargin(-mHeaderViewHeight);
         mFooterImageView.setVisibility(View.VISIBLE);
         mFooterImageView.setImageResource(R.drawable.ic_pulltorefresh_arrow_up);
         mFooterTextView.setText(R.string.pull_to_refresh_footer_pull_label);
         mFooterProgressBar.setVisibility(View.GONE);
         // mHeaderUpdateTextView.setText("");
         mFooterState = PULL_TO_REFRESH;
     }
     /**
      * Set a text to represent when the list was last updated.
      * @param lastUpdated Last updated at.
      */
     public void setLastUpdated(CharSequence lastUpdated) {
         if (lastUpdated != null) {
             mHeaderUpdateTextView.setVisibility(View.VISIBLE);
             mHeaderUpdateTextView.setText(lastUpdated);
         } else {
             mHeaderUpdateTextView.setVisibility(View.GONE);
         }
     }
     /**
      * 获取当前header view 的topMargin
      *
      * @description
      */
     private int getHeaderTopMargin() {
         LayoutParams params = (LayoutParams) mHeaderView.getLayoutParams();
         return params.topMargin;
     }
 
     /**
      * lock
      *
      */
     private void lock() {
         mLock = true;
     }
 
     /**
      * unlock
      *
      */
     private void unlock() {
         mLock = false;
     }
 
     /**
      * set headerRefreshListener
      *
      * @description
      * @param headerRefreshListener
      */
     public void setOnHeaderRefreshListener(OnHeaderRefreshListener headerRefreshListener) {
         mOnHeaderRefreshListener = headerRefreshListener;
     }
 
     public void setOnFooterRefreshListener(OnFooterRefreshListener footerRefreshListener) {
         mOnFooterRefreshListener = footerRefreshListener;
     }
 
     /**
      * Interface definition for a callback to be invoked when list/grid footer
      * view should be refreshed.
      */
     public interface OnFooterRefreshListener {
         public void onFooterRefresh(PullToRefreshView view);
     }
 
     /**
      * Interface definition for a callback to be invoked when list/grid header
      * view should be refreshed.
      */
     public  interface  OnHeaderRefreshListener {
         public  void  onHeaderRefresh(PullToRefreshView view);
     }
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值