-
Notifications
You must be signed in to change notification settings - Fork 8
/
OverScrollListView.java
160 lines (130 loc) · 4.98 KB
/
OverScrollListView.java
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
/*
* ----------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 42):
* <dejanr23@gmail.com> wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return.
* ----------------------------------------------------------------------------
*/
package com.fuzzydev.widget;
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.AbsListView;
import android.widget.EdgeEffect;
import android.widget.ListView;
import java.lang.reflect.Field;
;
/**
* Naive ListView subclass that allows you to change the over scroll value and hook
* a listener that will tell you how far you have over scrolled. It also disables
* the glow effect at the top edge. The disabling is done by reflection, so it may
* at some point stop working if the field name changes, but a a global has been set
* for quick modification if need be.
* <p>
* <p>
* Created by Dejan Ristic on 1/5/15.
*/
public class OverScrollListView extends ListView {
private static final String TAG = "OverScrollListView";
private static final String TOP_EDGE_EFFECT_FIELD = "mEdgeGlowTop"; // Variable to change if field changes.
private static final int DEFAULT_MAX_Y = 150;
private int mMaxOverScrollY = DEFAULT_MAX_Y;
private boolean didStartOverScroll = false;
private boolean didFinishOverScroll = false;
private boolean isClamped;
private EdgeEffect mTopEdgeEffect;
private OverScrolledListener mListener;
public interface OverScrolledListener {
void overScrolled(int scrollY, int maxY, boolean exceededOffset, boolean didFinishOverScroll);
}
public OverScrollListView(Context context) {
super(context);
init();
}
public OverScrollListView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public OverScrollListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
public void setOverScrollListener(OverScrolledListener listener) {
mListener = listener;
}
public void setOverScrollOffsetY(int offset) {
mMaxOverScrollY = offset;
}
private void init() {
setFadingEdgeLength(0);
setVerticalFadingEdgeEnabled(false);
getPrivateFieldMembers();
}
private void getPrivateFieldMembers() {
try {
mTopEdgeEffect = getTopEdgeEffect();
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
Log.e(TAG, "The Reflection Failed! Check if the field name changed in AbsListView.java inside the AOSP!");
}
}
private EdgeEffect getTopEdgeEffect() throws NoSuchFieldException, IllegalAccessException {
Field f = AbsListView.class.getDeclaredField(TOP_EDGE_EFFECT_FIELD);
if (f != null) {
f.setAccessible(true);
return (EdgeEffect) f.get(this);
}
return null;
}
private void reset() {
smoothScrollToPosition(0);
didFinishOverScroll = true;
didStartOverScroll = false;
mListener.overScrolled(0, mMaxOverScrollY, false, true);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_UP:
if (didStartOverScroll) {
if (isClamped) {
reset();
return true;
}
}
break;
}
return super.onTouchEvent(ev);
}
@Override
protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {
return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, maxOverScrollX, mMaxOverScrollY, isTouchEvent);
}
@Override
protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) {
super.onOverScrolled(scrollX, scrollY, clampedX, clampedY);
if (scrollY < 0 && !didStartOverScroll) {
didStartOverScroll = true;
didFinishOverScroll = false;
}
if (scrollY == 0 && didStartOverScroll) {
didStartOverScroll = false;
didFinishOverScroll = true;
}
if (mListener != null && scrollY < 1) {
mListener.overScrolled(Math.abs(scrollY), mMaxOverScrollY, clampedY, didFinishOverScroll);
} else {
Log.v(TAG, "No scroll listener set");
}
}
@Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mTopEdgeEffect != null) {
mTopEdgeEffect.finish();
}
}
}