Skip to content

Commit a9fa773

Browse files
committed
the class
the overscroll listview class
1 parent e1f1ee4 commit a9fa773

File tree

1 file changed

+265
-0
lines changed

1 file changed

+265
-0
lines changed
Lines changed: 265 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,265 @@
1+
package com.larphoid.overscrollinglistview;
2+
3+
import android.content.Context;
4+
import android.os.Handler;
5+
import android.util.AttributeSet;
6+
import android.view.GestureDetector;
7+
import android.view.MotionEvent;
8+
import android.view.View;
9+
import android.view.GestureDetector.OnGestureListener;
10+
import android.view.WindowManager;
11+
import android.widget.AbsListView;
12+
import android.widget.AdapterView;
13+
import android.widget.ListView;
14+
import android.widget.AbsListView.OnScrollListener;
15+
16+
public class OverscrollListview extends ListView implements OnScrollListener, View.OnTouchListener, android.widget.AdapterView.OnItemSelectedListener {
17+
public Handler mHandler = new Handler();
18+
private View measure;
19+
public int nHeaders = 1, nFooters = 1, divHeight, delay = 10;
20+
public boolean rebound = false;
21+
private int firstVis, visibleCnt, lastVis, totalItems, scrollstate;
22+
private boolean bounce = true, recalcV = false, trackballEvent = false;
23+
private long flingTimestamp = 0;
24+
private float velocity = 0;
25+
private static final float BREAKSPEED = 4.0f, BOUNCEBRAKE = -.5f;
26+
private GestureDetector gesture;
27+
28+
public OverscrollListview(Context context) {
29+
super(context);
30+
initialize(context);
31+
}
32+
33+
public OverscrollListview(Context context, AttributeSet attrs) {
34+
super(context, attrs);
35+
initialize(context);
36+
}
37+
38+
public OverscrollListview(Context context, AttributeSet attrs, int defStyle) {
39+
super(context, attrs, defStyle);
40+
initialize(context);
41+
}
42+
43+
@Override
44+
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
45+
firstVis = firstVisibleItem;
46+
visibleCnt = visibleItemCount;
47+
totalItems = totalItemCount;
48+
lastVis = firstVisibleItem + visibleItemCount;
49+
}
50+
51+
@Override
52+
public void onScrollStateChanged(AbsListView view, int scrollState) {
53+
scrollstate = scrollState;
54+
if (scrollState != OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
55+
rebound = true;
56+
mHandler.postDelayed(checkListviewTopAndBottom, delay);
57+
}
58+
}
59+
60+
@Override
61+
public void onItemSelected(AdapterView<?> av, View v, int position, long id) {
62+
rebound = true;
63+
mHandler.postDelayed(checkListviewTopAndBottom, delay);
64+
}
65+
66+
@Override
67+
public void onNothingSelected(AdapterView<?> av) {
68+
rebound = true;
69+
mHandler.postDelayed(checkListviewTopAndBottom, delay);
70+
}
71+
72+
@Override
73+
public boolean onTrackballEvent(MotionEvent event) {
74+
trackballEvent = true;
75+
rebound = true;
76+
mHandler.postDelayed(checkListviewTopAndBottom, delay);
77+
return super.onTrackballEvent(event);
78+
}
79+
80+
@Override
81+
public boolean onTouch(View v, MotionEvent event) {
82+
gesture.onTouchEvent(event);
83+
return false;
84+
}
85+
86+
private class thisGestureListener implements OnGestureListener {
87+
@Override
88+
public boolean onDown(MotionEvent e) {
89+
rebound = false;
90+
recalcV = false;
91+
velocity = 0;
92+
return false;
93+
}
94+
95+
@Override
96+
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
97+
rebound = true;
98+
recalcV = true;
99+
velocity = velocityY / 25.0f;
100+
flingTimestamp = System.currentTimeMillis();
101+
return false;
102+
}
103+
104+
@Override
105+
public void onLongPress(MotionEvent e) {
106+
}
107+
108+
@Override
109+
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
110+
return false;
111+
}
112+
113+
@Override
114+
public void onShowPress(MotionEvent e) {
115+
}
116+
117+
@Override
118+
public boolean onSingleTapUp(MotionEvent e) {
119+
rebound = true;
120+
recalcV = false;
121+
velocity = 0;
122+
return false;
123+
}
124+
};
125+
126+
public void initialize(Context context) {
127+
View header = new View(context);
128+
header.setMinimumHeight(((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getHeight());
129+
addHeaderView(header, null, false);
130+
addFooterView(header, null, false);
131+
132+
gesture = new GestureDetector(new thisGestureListener());
133+
gesture.setIsLongpressEnabled(false);
134+
flingTimestamp = System.currentTimeMillis();
135+
setHeaderDividersEnabled(false);
136+
setFooterDividersEnabled(false);
137+
setOnTouchListener(this);
138+
setOnScrollListener(this);
139+
setOnItemSelectedListener(this);
140+
}
141+
142+
public void initializeValues() {
143+
nHeaders = getHeaderViewsCount();
144+
nFooters = getFooterViewsCount();
145+
divHeight = getDividerHeight();
146+
firstVis = 0;
147+
visibleCnt = 0;
148+
lastVis = 0;
149+
totalItems = 0;
150+
scrollstate = 0;
151+
}
152+
153+
public void setBounce(boolean bouncing) {
154+
bounce = bouncing;
155+
}
156+
157+
public Runnable checkListviewTopAndBottom = new Runnable() {
158+
@Override
159+
public void run() {
160+
mHandler.removeCallbacks(checkListviewTopAndBottom);
161+
162+
if (rebound) {
163+
164+
if (trackballEvent && firstVis < nHeaders && lastVis >= totalItems) {
165+
trackballEvent = false;
166+
rebound = false;
167+
return;
168+
}
169+
170+
if (firstVis < nHeaders) {
171+
172+
// hack to avoid strange behaviour when there aren't enough items to fill the entire listview
173+
if (lastVis >= totalItems) {
174+
smoothScrollBy(0, 0);
175+
rebound = false;
176+
recalcV = false;
177+
velocity = 0;
178+
}
179+
180+
if (recalcV) {
181+
recalcV = false;
182+
velocity /= (1.0f + ((System.currentTimeMillis() - flingTimestamp) / 1000.0f));
183+
}
184+
if (firstVis == nHeaders) {
185+
recalcV = false;
186+
}
187+
measure = getChildAt(nHeaders);
188+
try {
189+
if (measure.getTop() + velocity < divHeight) {
190+
velocity *= BOUNCEBRAKE;
191+
if (!bounce || Math.abs(velocity) < 1.0f) {
192+
rebound = false;
193+
recalcV = false;
194+
velocity = 0;
195+
} else {
196+
setSelectionFromTop(nHeaders, divHeight - 1);
197+
}
198+
}
199+
} catch (Exception e) {
200+
if (velocity > 0) velocity = -velocity;
201+
}
202+
if (rebound) {
203+
smoothScrollBy((int) -velocity, 0);
204+
if (velocity > BREAKSPEED) velocity /= BREAKSPEED;
205+
else velocity -= BREAKSPEED;
206+
}
207+
208+
} else if (lastVis >= totalItems) {
209+
210+
if (recalcV) {
211+
recalcV = false;
212+
velocity /= (1.0f + ((System.currentTimeMillis() - flingTimestamp) / 1000.0f));
213+
}
214+
if (lastVis == totalItems - nHeaders - nFooters) {
215+
rebound = false;
216+
recalcV = false;
217+
velocity = 0;
218+
} else {
219+
measure = getChildAt(visibleCnt - nHeaders - nFooters);
220+
try {
221+
if (measure.getBottom() + velocity > getHeight() - divHeight) {
222+
velocity *= BOUNCEBRAKE;
223+
if (!bounce || Math.abs(velocity) < 1.0f) {
224+
rebound = false;
225+
recalcV = false;
226+
velocity = 0;
227+
} else {
228+
setSelectionFromTop(lastVis - nHeaders - nFooters, getHeight() - divHeight - measure.getHeight() + 1);
229+
}
230+
}
231+
} catch (Exception e) {
232+
if (velocity < 0) velocity = -velocity;
233+
}
234+
}
235+
if (rebound) {
236+
smoothScrollBy((int) -velocity, 0);
237+
if (velocity < -BREAKSPEED) velocity /= BREAKSPEED;
238+
else velocity += BREAKSPEED;
239+
}
240+
241+
} else if (scrollstate == OnScrollListener.SCROLL_STATE_IDLE) {
242+
243+
rebound = false;
244+
recalcV = false;
245+
velocity = 0;
246+
}
247+
mHandler.postDelayed(checkListviewTopAndBottom, delay);
248+
return;
249+
}
250+
251+
if (scrollstate != OnScrollListener.SCROLL_STATE_IDLE) return;
252+
253+
if (totalItems == (nHeaders + nFooters) || firstVis < nHeaders) {
254+
setSelectionFromTop(nHeaders, divHeight);
255+
smoothScrollBy(0, 0);
256+
} else if (lastVis == totalItems) {
257+
int offset = getHeight() - divHeight;
258+
measure = getChildAt(visibleCnt - nHeaders - nFooters);
259+
if (measure != null) offset -= measure.getHeight();
260+
setSelectionFromTop(lastVis - nHeaders - nFooters, offset);
261+
smoothScrollBy(0, 0);
262+
}
263+
}
264+
};
265+
}

0 commit comments

Comments
 (0)