Is there a way to detect if a ListView is fast scrolling? That is to say, can we somehow know when the user presses and releases the FastScroller?
Asked
Active
Viewed 1,997 times
2 Answers
6
Reflection allows you to do it. By taking a look at the AbsListView source code, there is a FastScroller
object which indicates a wrapper around the fast scrolling functionality. Its source code shows an interesting field:
/**
* Current decoration state, one of:
* <ul>
* <li>{@link #STATE_NONE}, nothing visible
* <li>{@link #STATE_VISIBLE}, showing track and thumb
* <li>{@link #STATE_DRAGGING}, visible and showing preview
* </ul>
*/
private int mState;
This field contains the status of the FastScroller
object. The solution consists in reading this field's value via reflection, every time the onScroll()
and onScrollStateChanged()
methods are triggered.
This code implements the solution described above:
private class CustomScrollListener implements OnScrollListener {
private ListView list;
private int mState = -1;
private Field stateField = null;
private Object mFastScroller;
private int STATE_DRAGGING;
public CustomScrollListener() {
super();
String fastScrollFieldName = "mFastScroller";
// this has changed on Lollipop
if (Build.VERSION.SDK_INT >= 21) {
fastScrollFieldName = "mFastScroll";
}
try {
Field fastScrollerField = AbsListView.class.getDeclaredField(fastScrollFieldName);
fastScrollerField.setAccessible(true);
mFastScroller = fastScrollerField.get(list);
Field stateDraggingField = mFastScroller.getClass().getDeclaredField("STATE_DRAGGING");
stateDraggingField.setAccessible(true);
STATE_DRAGGING = stateDraggingField.getInt(mFastScroller);
stateField = mFastScroller.getClass().getDeclaredField("mState");
stateField.setAccessible(true);
mState = stateField.getInt(mFastScroller);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
// update fast scroll state
try {
if (stateField != null) {
mState = stateField.getInt(mFastScroller);
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
if (mState == STATE_DRAGGING)) {
// the user is fast scrolling through the list
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
// update fast scroll state
try {
if (stateField != null) {
mState = stateField.getInt(mFastScroller);
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}

Sebastiano
- 12,289
- 6
- 47
- 80
-
what is the field "grid" in fastScrollerField.get(grid); Also is there any disadvantages to using this, i assume reflections makes this 'slow' – toidiu Mar 22 '15 at 08:07
-
`grid` is the GridView that I was using. I'm changing that to `list`, so to be more clear. – Sebastiano Mar 22 '15 at 08:08
-
Also assuming this might break if the source is changed from sdk to sdk? and therefore the try catch. seems like a hacky solution – toidiu Mar 22 '15 at 08:14
-
Yes, it is hacky, and yes, it has changed already, but only once. I'm updating the answer with the two variants. IMO, this is the only viable approach. – Sebastiano Mar 22 '15 at 08:16
-
Right so it works! Brilliant! +1 But like you said it has changed so that the field variable is now "mFastScroll". Got to be a better way hummm – toidiu Mar 22 '15 at 08:18
-
The thing is that now we have `RecyclerView` as the main list, and that does not support fast scrolling out of the box. Mark Hallison is writing a nice series of articles on this very subject: http://blog.stylingandroid.com/recyclerview-fastscroll-part-1/ – Sebastiano Mar 22 '15 at 08:20
-
This unfortunately does not work anymore on API 28 (Pie) if using targetSdkVersion=28, because "mState" is on the dark grey-list, resulting in NoSuchFieldException when trying to access that field. – b0b Aug 28 '19 at 07:26
0
Try using the setOnScrollListener and implement the onScrollStateChanged. The scrollState can be idle, touch scroll, or fling
setOnScrollListener(new OnScrollListener(){
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
// TODO Auto-generated method stub
}
public void onScrollStateChanged(AbsListView view, int scrollState) {
// TODO Auto-generated method stub
if(scrollState == 2) Log.i("a", "fast scroll");
}
});
}

Or Bar
- 1,566
- 11
- 12
-
This doesn't mean it's fast tho. This will trigger scroll even if the user flings with little acceleration. – Martin Marconcini Jan 26 '14 at 00:13
-
-
I tried that already and scrolling touching the screen or scrolling the fastScroller return the same scrollState. I am using JazzyListView with a SLIDE_IN effect and have fast scrolling enabled in the ListView. When I fast scroll it does not work quite right because the first or last visible items are many times not visible. I wanted disable the effect when entering the fast scroll mode and re-enable it when leaving it. – mollymay Jan 26 '14 at 22:13
-
It appears like there isn't a way to determine when fast scroll is used. there is a [bug report](https://code.google.com/p/android/issues/detail?id=7393) about it that says a fix is already released, but I can't find anything in the docs about it. – Or Bar Jan 26 '14 at 23:19