我做了一个大集合的demo,实现了ListView、GridView、ExpandableListView、ScrollView、WebView、ImageView、TextView的下拉刷新和上拉加载。后面会提供demo的下载地址。
依照惯例,下面将会是一大波效果图:
demo首页也是可下拉的ListView,在底下可以加入table:
ListView:
GridView:
ExpandableListView:
ScrollView:
WebView:
ImageView:
TextView:
很不错吧?最后的ImageView和TextView是最简单的,直接在下面的接口方法里返回true。
增加上拉加载很简单,和管理下拉头一样,再多管理一个上拉头,也不费事;至于把它改成通用的就需要统一一下View的行为了,为此,我定义了这样一个接口:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
package
com.jingchen.pulltorefresh.pullableview;
public
interface
Pullable
{
/**
* 判断是否可以下拉,如果不需要下拉功能可以直接return false
*
* @return true如果可以下拉否则返回false
*/
boolean
canPullDown();
/**
* 判断是否可以上拉,如果不需要上拉功能可以直接return false
*
* @return true如果可以上拉否则返回false
*/
boolean
canPullUp();
}
|
1、增加了上拉头,相应的也增加了控制变量。
2、拉动时消除content_view事件防止误触发不再使用反射,直接设置 event.setAction(MotionEvent.ACTION_CANCEL)。
3、消除了拉动过程中的多点触碰导致的剧变。
4、不再设置content_view的onTouListener,让使用者可以更加自由的设置监听器。
这个PullToRefreshLayout只负责管理三个控件,如果一个View需要有上拉下拉功能则只需实现接口就行了。下面看PullToRefreshLayout的代码,注释写了好多:
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
583
584
585
586
587
588
|
package
com.jingchen.pulltorefresh;
import
java.util.Timer;
import
java.util.TimerTask;
import
android.content.Context;
import
android.graphics.Canvas;
import
android.graphics.LinearGradient;
import
android.graphics.Paint;
import
android.graphics.Paint.Style;
import
android.graphics.RectF;
import
android.graphics.Shader.TileMode;
import
android.os.Handler;
import
android.os.Message;
import
android.util.AttributeSet;
import
android.util.Log;
import
android.view.MotionEvent;
import
android.view.View;
import
android.view.ViewGroup;
import
android.view.animation.AnimationUtils;
import
android.view.animation.LinearInterpolator;
import
android.view.animation.RotateAnimation;
import
android.widget.RelativeLayout;
import
android.widget.TextView;
import
com.jingchen.pulltorefresh.pullableview.Pullable;
/**
* 自定义的布局,用来管理三个子控件,其中一个是下拉头,一个是包含内容的pullableView(可以是实现Pullable接口的的任何View),
* 还有一个上拉头
*
* @author 陈靖
*/
public
class
PullToRefreshLayout
extends
RelativeLayout
{
public
static
final
String TAG = PullToRefreshLayout;
// 初始状态
public
static
final
int
INIT =
0
;
// 释放刷新
public
static
final
int
RELEASE_TO_REFRESH =
1
;
// 正在刷新
public
static
final
int
REFRESHING =
2
;
// 释放加载
public
static
final
int
RELEASE_TO_LOAD =
3
;
// 正在加载
public
static
final
int
LOADING =
4
;
// 操作完毕
public
static
final
int
DONE =
5
;
// 当前状态
private
int
state = INIT;
// 刷新回调接口
private
OnRefreshListener mListener;
// 刷新成功
public
static
final
int
SUCCEED =
0
;
// 刷新失败
public
static
final
int
FAIL =
1
;
// 按下Y坐标,上一个事件点Y坐标
private
float
downY, lastY;
// 下拉的距离。注意:pullDownY和pullUpY不可能同时不为0
public
float
pullDownY =
0
;
// 上拉的距离
private
float
pullUpY =
0
;
// 释放刷新的距离
private
float
refreshDist =
200
;
// 释放加载的距离
private
float
loadmoreDist =
200
;
private
MyTimer timer;
// 回滚速度
public
float
MOVE_SPEED =
8
;
// 第一次执行布局
private
boolean
isLayout =
false
;
// 在刷新过程中滑动操作
private
boolean
isTouch =
false
;
// 手指滑动距离与下拉头的滑动距离比,中间会随正切函数变化
private
float
radio =
2
;
// 下拉箭头的转180°动画
private
RotateAnimation rotateAnimation;
// 均匀旋转动画
private
RotateAnimation refreshingAnimation;
// 下拉头
private
View refreshView;
// 下拉的箭头
private
View pullView;
// 正在刷新的图标
private
View refreshingView;
// 刷新结果图标
private
View refreshStateImageView;
// 刷新结果:成功或失败
private
TextView refreshStateTextView;
// 上拉头
private
View loadmoreView;
// 上拉的箭头
private
View pullUpView;
// 正在加载的图标
private
View loadingView;
// 加载结果图标
private
View loadStateImageView;
// 加载结果:成功或失败
private
TextView loadStateTextView;
// 实现了Pullable接口的View
private
View pullableView;
// 过滤多点触碰
private
int
mEvents;
// 这两个变量用来控制pull的方向,如果不加控制,当情况满足可上拉又可下拉时没法下拉
private
boolean
canPullDown =
true
;
private
boolean
canPullUp =
true
;
/**
* 执行自动回滚的handler
*/
Handler updateHandler =
new
Handler()
{
@Override
public
void
handleMessage(Message msg)
{
// 回弹速度随下拉距离moveDeltaY增大而增大
MOVE_SPEED = (
float
) (
8
+
5
* Math.tan(Math.PI /
2
/ getMeasuredHeight() * (pullDownY + Math.abs(pullUpY))));
if
(!isTouch)
{
// 正在刷新,且没有往上推的话则悬停,显示正在刷新...
if
(state == REFRESHING && pullDownY <= refreshDist)
{
pullDownY = refreshDist;
timer.cancel();
}
else
if
(state == LOADING && -pullUpY <= loadmoreDist)
{
pullUpY = -loadmoreDist;
timer.cancel();
}
}
if
(pullDownY >
0
)
pullDownY -= MOVE_SPEED;
else
if
(pullUpY <
0
)
pullUpY += MOVE_SPEED;
if
(pullDownY <
0
)
{
// 已完成回弹
pullDownY =
0
;
pullView.clearAnimation();
// 隐藏下拉头时有可能还在刷新,只有当前状态不是正在刷新时才改变状态
if
(state != REFRESHING && state != LOADING)
changeState(INIT);
timer.cancel();
}
if
(pullUpY >
0
)
{
// 已完成回弹
pullUpY =
0
;
pullUpView.clearAnimation();
// 隐藏下拉头时有可能还在刷新,只有当前状态不是正在刷新时才改变状态
if
(state != REFRESHING && state != LOADING)
changeState(INIT);
timer.cancel();
}
// 刷新布局,会自动调用onLayout
requestLayout();
}
};
public
void
setOnRefreshListener(OnRefreshListener listener)
{
mListener = listener;
}
public
PullToRefreshLayout(Context context)
{
super
(context);
initView(context);
}
public
PullToRefreshLayout(Context context, AttributeSet attrs)
{
super
(context, attrs);
initView(context);
}
public
PullToRefreshLayout(Context context, AttributeSet attrs,
int
defStyle)
{
super
(context, attrs, defStyle);
initView(context);
}
private
void
initView(Context context)
{
timer =
new
MyTimer(updateHandler);
rotateAnimation = (RotateAnimation) AnimationUtils.loadAnimation(
context, R.anim.reverse_anim);
refreshingAnimation = (RotateAnimation) AnimationUtils.loadAnimation(
context, R.anim.rotating);
// 添加匀速转动动画
LinearInterpolator lir =
new
LinearInterpolator();
rotateAnimation.setInterpolator(lir);
refreshingAnimation.setInterpolator(lir);
}
private
void
hide()
{
timer.schedule(
5
);
}
/**
* 完成刷新操作,显示刷新结果。注意:刷新完成后一定要调用这个方法
*/
/**
* @param refreshResult
* PullToRefreshLayout.SUCCEED代表成功,PullToRefreshLayout.FAIL代表失败
*/
public
void
refreshFinish(
int
refreshResult)
{
refreshingView.clearAnimation();
refreshingView.setVisibility(View.GONE);
switch
(refreshResult)
{
case
SUCCEED:
// 刷新成功
refreshStateImageView.setVisibility(View.VISIBLE);
refreshStateTextView.setText(R.string.refresh_succeed);
refreshStateImageView
.setBackgroundResource(R.drawable.refresh_succeed);
break
;
case
FAIL:
default
:
// 刷新失败
refreshStateImageView.setVisibility(View.VISIBLE);
refreshStateTextView.setText(R.string.refresh_fail);
refreshStateImageView
.setBackgroundResource(R.drawable.refresh_failed);
break
;
}
// 刷新结果停留1秒
new
Handler()
{
@Override
public
void
handleMessage(Message msg)
{
changeState(DONE);
hide();
}
}.sendEmptyMessageDelayed(
0
,
1000
);
}
/**
* 加载完毕,显示加载结果。注意:加载完成后一定要调用这个方法
*
* @param refreshResult
* PullToRefreshLayout.SUCCEED代表成功,PullToRefreshLayout.FAIL代表失败
*/
public
void
loadmoreFinish(
int
refreshResult)
{
loadingView.clearAnimation();
loadingView.setVisibility(View.GONE);
switch
(refreshResult)
{
case
SUCCEED:
// 加载成功
loadStateImageView.setVisibility(View.VISIBLE);
loadStateTextView.setText(R.string.load_succeed);
loadStateImageView.setBackgroundResource(R.drawable.load_succeed);
break
;
case
FAIL:
default
:
// 加载失败
loadStateImageView.setVisibility(View.VISIBLE);
loadStateTextView.setText(R.string.load_fail);
loadStateImageView.setBackgroundResource(R.drawable.load_failed);
break
;
}
// 刷新结果停留1秒
new
Handler()
{
@Override
public
void
handleMessage(Message msg)
{
changeState(DONE);
hide();
}
}.sendEmptyMessageDelayed(
0
,
1000
);
}
private
void
changeState(
int
to)
{
state = to;
switch
(state)
{
case
INIT:
// 下拉布局初始状态
refreshStateImageView.setVisibility(View.GONE);
refreshStateTextView.setText(R.string.pull_to_refresh);
pullView.clearAnimation();
pullView.setVisibility(View.VISIBLE);
// 上拉布局初始状态
loadStateImageView.setVisibility(View.GONE);
loadStateTextView.setText(R.string.pullup_to_load);
pullUpView.clearAnimation();
pullUpView.setVisibility(View.VISIBLE);
break
;
case
RELEASE_TO_REFRESH:
// 释放刷新状态
refreshStateTextView.setText(R.string.release_to_refresh);
pullView.startAnimation(rotateAnimation);
break
;
case
REFRESHING:
// 正在刷新状态
pullView.clearAnimation();
refreshingView.setVisibility(View.VISIBLE);
pullView.setVisibility(View.INVISIBLE);
refreshingView.startAnimation(refreshingAnimation);
refreshStateTextView.setText(R.string.refreshing);
break
;
case
RELEASE_TO_LOAD:
// 释放加载状态
loadStateTextView.setText(R.string.release_to_load);
pullUpView.startAnimation(rotateAnimation);
break
;
case
LOADING:
// 正在加载状态
pullUpView.clearAnimation();
loadingView.setVisibility(View.VISIBLE);
pullUpView.setVisibility(View.INVISIBLE);
loadingView.startAnimation(refreshingAnimation);
loadStateTextView.setText(R.string.loading);
break
;
case
DONE:
// 刷新或加载完毕,啥都不做
break
;
}
}
/**
* 不限制上拉或下拉
*/
private
void
releasePull()
{
canPullDown =
true
;
canPullUp =
true
;
}
/*
* (非 Javadoc)由父控件决定是否分发事件,防止事件冲突
*
* @see android.view.ViewGroup#dispatchTouchEvent(android.view.MotionEvent)
*/
@Override
public
boolean
dispatchTouchEvent(MotionEvent ev)
{
switch
(ev.getActionMasked())
{
case
MotionEvent.ACTION_DOWN:
downY = ev.getY();
lastY = downY;
timer.cancel();
mEvents =
0
;
releasePull();
break
;
case
MotionEvent.ACTION_POINTER_DOWN:
case
MotionEvent.ACTION_POINTER_UP:
// 过滤多点触碰
mEvents = -
1
;
break
;
case
MotionEvent.ACTION_MOVE:
if
(mEvents ==
0
)
{
if
(((Pullable) pullableView).canPullDown() && canPullDown
&& state != LOADING)
{
// 可以下拉,正在加载时不能下拉
// 对实际滑动距离做缩小,造成用力拉的感觉
pullDownY = pullDownY + (ev.getY() - lastY) / radio;
if
(pullDownY <
0
)
{
pullDownY =
0
;
canPullDown =
false
;
canPullUp =
true
;
}
if
(pullDownY > getMeasuredHeight())
pullDownY = getMeasuredHeight();
if
(state == REFRESHING)
{
// 正在刷新的时候触摸移动
isTouch =
true
;
}
}
else
if
(((Pullable) pullableView).canPullUp() && canPullUp
&& state != REFRESHING)
{
// 可以上拉,正在刷新时不能上拉
pullUpY = pullUpY + (ev.getY() - lastY) / radio;
if
(pullUpY >
0
)
{
pullUpY =
0
;
canPullDown =
true
;
canPullUp =
false
;
}
if
(pullUpY < -getMeasuredHeight())
pullUpY = -getMeasuredHeight();
if
(state == LOADING)
{
// 正在加载的时候触摸移动
isTouch =
true
;
}
}
else
releasePull();
}
else
mEvents =
0
;
lastY = ev.getY();
// 根据下拉距离改变比例
radio = (
float
) (
2
+
2
* Math.tan(Math.PI /
2
/ getMeasuredHeight()
* (pullDownY + Math.abs(pullUpY))));
requestLayout();
if
(pullDownY <= refreshDist && state == RELEASE_TO_REFRESH)
{
// 如果下拉距离没达到刷新的距离且当前状态是释放刷新,改变状态为下拉刷新
changeState(INIT);
}
if
(pullDownY >= refreshDist && state == INIT)
{
// 如果下拉距离达到刷新的距离且当前状态是初始状态刷新,改变状态为释放刷新
changeState(RELEASE_TO_REFRESH);
}
// 下面是判断上拉加载的,同上,注意pullUpY是负值
if
(-pullUpY <= loadmoreDist && state == RELEASE_TO_LOAD)
{
changeState(INIT);
}
if
(-pullUpY >= loadmoreDist && state == INIT)
{
changeState(RELEASE_TO_LOAD);
}
// 因为刷新和加载操作不能同时进行,所以pullDownY和pullUpY不会同时不为0,因此这里用(pullDownY +
// Math.abs(pullUpY))就可以不对当前状态作区分了
if
((pullDownY + Math.abs(pullUpY)) >
8
)
{
// 防止下拉过程中误触发长按事件和点击事件
ev.setAction(MotionEvent.ACTION_CANCEL);
}
break
;
case
MotionEvent.ACTION_UP:
if
(pullDownY > refreshDist || -pullUpY > loadmoreDist)
// 正在刷新时往下拉(正在加载时往上拉),释放后下拉头(上拉头)不隐藏
isTouch =
false
;
if
(state == RELEASE_TO_REFRESH)
{
changeState(REFRESHING);
// 刷新操作
if
(mListener !=
null
)
mListener.onRefresh(
this
);
}
else
if
(state == RELEASE_TO_LOAD)
{
changeState(LOADING);
// 加载操作
if
(mListener !=
null
)
mListener.onLoadMore(
this
);
}
hide();
default
:
break
;
}
// 事件分发交给父类
super
.dispatchTouchEvent(ev);
return
true
;
}
private
void
initView()
{
// 初始化下拉布局
pullView = refreshView.findViewById(R.id.pull_icon);
refreshStateTextView = (TextView) refreshView
.findViewById(R.id.state_tv);
refreshingView = refreshView.findViewById(R.id.refreshing_icon);
refreshStateImageView = refreshView.findViewById(R.id.state_iv);
// 初始化上拉布局
pullUpView = loadmoreView.findViewById(R.id.pullup_icon);
loadStateTextView = (TextView) loadmoreView
.findViewById(R.id.loadstate_tv);
loadingView = loadmoreView.findViewById(R.id.loading_icon);
loadStateImageView = loadmoreView.findViewById(R.id.loadstate_iv);
}
@Override
protected
void
onLayout(
boolean
changed,
int
l,
int
t,
int
r,
int
b)
{
if
(!isLayout)
{
// 这里是第一次进来的时候做一些初始化
refreshView = getChildAt(
0
);
pullableView = getChildAt(
1
);
loadmoreView = getChildAt(
2
);
isLayout =
true
;
initView();
refreshDist = ((ViewGroup) refreshView).getChildAt(
0
)
.getMeasuredHeight();
loadmoreDist = ((ViewGroup) loadmoreView).getChildAt(
0
)
.getMeasuredHeight();
}
// 改变子控件的布局,这里直接用(pullDownY + pullUpY)作为偏移量,这样就可以不对当前状态作区分
refreshView.layout(
0
,
(
int
) (pullDownY + pullUpY) - refreshView.getMeasuredHeight(),
refreshView.getMeasuredWidth(), (
int
) (pullDownY + pullUpY));
pullableView.layout(
0
, (
int
) (pullDownY + pullUpY),
pullableView.getMeasuredWidth(), (
int
) (pullDownY + pullUpY)
+ pullableView.getMeasuredHeight());
loadmoreView.layout(
0
,
(
int
) (pullDownY + pullUpY) + pullableView.getMeasuredHeight(),
loadmoreView.getMeasuredWidth(),
(
int
) (pullDownY + pullUpY) + pullableView.getMeasuredHeight()
+ loadmoreView.getMeasuredHeight());
}
class
MyTimer
{
private
Handler handler;
private
Timer timer;
private
MyTask mTask;
public
MyTimer(Handler handler)
{
this
.handler = handler;
timer =
new
Timer();
}
public
void
schedule(
long
period)
{
if
(mTask !=
null
)
{
mTask.cancel();
mTask =
null
;
}
mTask =
new
MyTask(handler);
timer.schedule(mTask,
0
, period);
}
public
void
cancel()
{
if
(mTask !=
null
)
{
mTask.cancel();
mTask =
null
;
}
}
class
MyTask
extends
TimerTask
{
private
Handler handler;
public
MyTask(Handler handler)
{
this
.handler = handler;
}
@Override
public
void
run()
{
handler.obtainMessage().sendToTarget();
}
}
}
/**
* 刷新加载回调接口
*
* @author chenjing
*
*/
public
interface
OnRefreshListener
{
/**
* 刷新操作
*/
void
onRefresh(PullToRefreshLayout pullToRefreshLayout);
/**
* 加载操作
*/
void
onLoadMore(PullToRefreshLayout pullToRefreshLayout);
}
}
|
下面看各个View对Pullable接口的实现,ListView和GridView还有ExpandableListView的判断方法是一样的:
PullableListView:
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
|
package
com.jingchen.pulltorefresh.pullableview;
import
android.content.Context;
import
android.util.AttributeSet;
import
android.util.Log;
import
android.widget.ListView;
public
class
PullableListView
extends
ListView
implements
Pullable
{
public
PullableListView(Context context)
{
super
(context);
}
public
PullableListView(Context context, AttributeSet attrs)
{
super
(context, attrs);
}
public
PullableListView(Context context, AttributeSet attrs,
int
defStyle)
{
super
(context, attrs, defStyle);
}
@Override
public
boolean
canPullDown()
{
if
(getCount() ==
0
)
{
// 没有item的时候也可以下拉刷新
return
true
;
}
else
if
(getFirstVisiblePosition() ==
0
&& getChildAt(
0
).getTop() >=
0
)
{
// 滑到ListView的顶部了
return
true
;
}
else
return
false
;
}
@Override
public
boolean
canPullUp()
{
if
(getCount() ==
0
)
{
// 没有item的时候也可以上拉加载
return
true
;
}
else
if
(getLastVisiblePosition() == (getCount() -
1
))
{
// 滑到底部了
if
(getChildAt(getLastVisiblePosition() - getFirstVisiblePosition()) !=
null
&& getChildAt(
getLastVisiblePosition()
- getFirstVisiblePosition()).getBottom() <= getMeasuredHeight())
return
true
;
}
return
false
;
}
}
|
PullableGridView:
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
|
package
com.jingchen.pulltorefresh.pullableview;
import
android.content.Context;
import
android.util.AttributeSet;
import
android.widget.GridView;
public
class
PullableGridView
extends
GridView
implements
Pullable
{
public
PullableGridView(Context context)
{
super
(context);
}
public
PullableGridView(Context context, AttributeSet attrs)
{
super
(context, attrs);
}
public
PullableGridView(Context context, AttributeSet attrs,
int
defStyle)
{
super
(context, attrs, defStyle);
}
@Override
public
boolean
canPullDown()
{
if
(getCount() ==
0
)
{
// 没有item的时候也可以下拉刷新
return
true
;
}
else
if
(getFirstVisiblePosition() ==
0
&& getChildAt(
0
).getTop() >=
0
)
{
// 滑到顶部了
return
true
;
}
else
return
false
;
}
@Override
public
boolean
canPullUp()
{
if
(getCount() ==
0
)
{
// 没有item的时候也可以上拉加载
return
true
;
}
else
if
(getLastVisiblePosition() == (getCount() -
1
))
{
// 滑到底部了
if
(getChildAt(getLastVisiblePosition() - getFirstVisiblePosition()) !=
null
&& getChildAt(
getLastVisiblePosition()
- getFirstVisiblePosition()).getBottom() <= getMeasuredHeight())
return
true
;
}
return
false
;
}
}
|
PullableScrollView:
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
|
package
com.jingchen.pulltorefresh.pullableview;
import
android.content.Context;
import
android.util.AttributeSet;
import
android.widget.ScrollView;
public
class
PullableScrollView
extends
ScrollView
implements
Pullable
{
public
PullableScrollView(Context context)
{
super
(context);
}
public
PullableScrollView(Context context, AttributeSet attrs)
{
super
(context, attrs);
}
public
PullableScrollView(Context context, AttributeSet attrs,
int
defStyle)
{
super
(context, attrs, defStyle);
}
@Override
public
boolean
canPullDown()
{
if
(getScrollY() ==
0
)
return
true
;
else
return
false
;
}
@Override
public
boolean
canPullUp()
{
if
(getScrollY() >= (getChildAt(
0
).getHeight() - getMeasuredHeight()))
return
true
;
else
return
false
;
}
}
|
OK了,整个demo的代码有点多,就不贴了。
源码下载:https://github.com/jingchenUSTC/PullToRefreshAndLoad