Reputation: 37144
In my app I have a ListView backed by ArrayAdapter. In it I'm detecting events in OnScrollListener#onScroll method to find end of the list. I noticed that on the phone (MyTouch) both track-ball and gesture/touch scrolling will trigger the event twice. On the emulator I get the same behavior with a scrolling wheel and click-and-drug scrolling. However in emulator if I use down-arrow button to scroll the event is fired only once.
Here's the code:
this.view.setOnScrollListener(new OnScrollListener() {
@Override
public void onScroll(final AbsListView view, final int first,
final int visible, final int total) {
// detect if last item is visible
if (visible < total && (first + visible == total)) {
Log.d("OnScrollListener - end of list", "fvi: " +
first + ", vic: " + visible + ", tic: " + total);
// this line gets called twice
onLastListItemDisplayed(total, visible);
}
}
}
How do I suppress or handle this behavior? I need just a single event and trying not to revert to silly hacks such as boolean field.
As far as I can tell - both events have identical stacktrace
Thread [<3> main] (Suspended (breakpoint at line 116 in SearchResultsView$4))
SearchResultsView$4.onScroll(AbsListView, int, int, int) line: 116
ListView(AbsListView).invokeOnItemScrollListener() line: 655
ListView.arrowScrollImpl(int) line: 2256
ListView.arrowScroll(int) line: 2172
ListView.commonKey(int, int, KeyEvent) line: 1977
ListView.onKeyMultiple(int, int, KeyEvent) line: 1929
KeyEvent.dispatch(KeyEvent$Callback) line: 899
ListView(View).dispatchKeyEvent(KeyEvent) line: 3647
ListView(ViewGroup).dispatchKeyEvent(KeyEvent) line: 744
ListView.dispatchKeyEvent(KeyEvent) line: 1909
FrameLayout(ViewGroup).dispatchKeyEvent(KeyEvent) line: 746
LinearLayout(ViewGroup).dispatchKeyEvent(KeyEvent) line: 746
PhoneWindow$DecorView(ViewGroup).dispatchKeyEvent(KeyEvent) line: 746
PhoneWindow$DecorView.superDispatchKeyEvent(KeyEvent) line: 1708
PhoneWindow.superDispatchKeyEvent(KeyEvent) line: 1197
SearchResultsView(Activity).dispatchKeyEvent(KeyEvent) line: 1967
PhoneWindow$DecorView.dispatchKeyEvent(KeyEvent) line: 1684
ViewRoot.deliverKeyEventToViewHierarchy(KeyEvent, boolean) line: 2329
ViewRoot.handleFinishedEvent(int, boolean) line: 2299
ViewRoot.handleMessage(Message) line: 1621
ViewRoot(Handler).dispatchMessage(Message) line: 99
Looper.loop() line: 123
ActivityThread.main(String[]) line: 4203
Method.invokeNative(Object, Object[], Class, Class[], Class, int, boolean) line: not available [native method]
Method.invoke(Object, Object...) line: 521
ZygoteInit$MethodAndArgsCaller.run() line: 791
ZygoteInit.main(String[]) line: 549
NativeStart.main(String[]) line: not available [native method]
P.S. Bug is created
P.P.S. And closed with very useful reply The trackball and the touch screen send several motion events very rapidly. This behavior is expected.
Upvotes: 4
Views: 3769
Reputation: 1318
I've got also this problem with multiple events firing of while I was scrolling through the ListView. I've intended to load more ListItems that moment when I was passing a treshold value. However, this always resulted in multiple called AsyncTasks which populated my ListView faster than wanted.
I've found a good solution in this blogpost. It basically includes a check for a boolean, although you dislike this solution. I find it also some kind of hacky but I did not came up with another solution though.
Upvotes: 0
Reputation: 37144
Since good people of Google are saying that this is as intended
here's an ugly brutal way of dealing with this problem that works for me. Basically the assumption here is that as events are issued I compare last saved first item index with one that is passed in the onScroll
call. I will only process event if these do not match:
this.view.setOnScrollListener(new OnScrollListener() {
private int lastSavedFirst = -1;
@Override
public void onScroll(final AbsListView view, final int first, final int visible, final int total) {
// detect if last item is visible
if (visible < total && (first + visible == total)) {
// only process first event
if (first != lastSavedFirst) {
lastSavedFirst = first;
Log.d("OnScrollListener - end of list", "fvi: " + first + ", vic: " + visible + ", tic: "
+ total);
onLastListItemDisplayed(total, visible);
}
}
}
@Override
public void onScrollStateChanged(final AbsListView view, final int scrollState) {
// Log.d("OnScrollListener", "state: " + scrollState);
}
});
Upvotes: 4