Android 中 ScrollView 监听滚动停止的方案

前几天工作上遇到一个需要监听 Scrollview 滚动停止的问题,然后开始谷歌解决方案,但是都不是很满意,最后在和同事的讨论中获得灵感得出了下面的解决方案,经实测有效,供大家参考一下。

思路

思路是这样的:首先要确定监听的时机,因为 ScrollView 在滚动的过程中和绘制的时候需要 computeScroll,所以在 computeScroll 的时候监听; 然后用反射检测滚动是否停止; 最后由于工作的需求是在 Touch 的情况下都不算停止所以还要监听 touch event。

其中有用到反射,对反射不是很了解的可以看我的这篇博客 Java 反射机制

Code

关键代码如下:

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
import android.content.Context;
import android.view.MotionEvent;
import android.widget.OverScroller;
import android.widget.ScrollView;
/**
* @author Johnny Shieh
* @date 2015-07-17
* @description Custom ScrollView which can detect when scroll stop.
*/
public class MyScrollView extends ScrollView {
/**
* 覆盖默认的 scroller,方便调用 isFinished 方法
*/
private OverScroller mScroller;
/**
* 判断是否按下的标记位
*/
private boolean mInTouch = false;
public MyScrollView(Context context) {
super(context);
setMyScroller(context);
}
public MyScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
setMyScroller(context);
}
public MyScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setMyScroller(context);
}
private void setMyScroller(Context context) {
mScroller = new OverScroller(context);
ReflectionUtils.setField(this, "android.widget.ScrollView", "mScroller", mScroller);
}
/**
* 检测滚动是否停止的方法
*/
private void checkScrollStop() {
boolean mIsBeingDragged = (Boolean)ReflectionUtils.getField(this, "android.widget.ScrollView", "mIsBeingDragged");
if(mScroller.isFinished() && !mIsBeingDragged && !mInTouch) {
// scroll is stoped
// TODO do something
}
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
boolean result = super.onTouchEvent(ev);
final int actionMasked = ev.getActionMasked();
switch (actionMasked) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
mInTouch = true;
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
mInTouch = false;
checkScrollStop();
break;
default:
break;
}
return result;
}
@Override
public void computeScroll() {
super.computeScroll();
checkScrollStop();
}
}