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?
How to detect if a ListView is fast scrolling
Asked Answered
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();
}
}
}
what is the field "grid" in fastScrollerField.get(grid); Also is there any disadvantages to using this, i assume reflections makes this 'slow' –
Mellissamellitz
grid
is the GridView that I was using. I'm changing that to list
, so to be more clear. –
Upsilon Also assuming this might break if the source is changed from sdk to sdk? and therefore the try catch. seems like a hacky solution –
Mellissamellitz
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. –
Upsilon
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 –
Mellissamellitz
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: blog.stylingandroid.com/recyclerview-fastscroll-part-1 –
Upsilon 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. –
Entertainment
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");
}
});
}
This doesn't mean it's fast tho. This will trigger scroll even if the user flings with little acceleration. –
Jonejonell
Are you trying to determine how fast it's scrolling? –
Skardol
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. –
Juryrigged
It appears like there isn't a way to determine when fast scroll is used. there is a bug report about it that says a fix is already released, but I can't find anything in the docs about it. –
Skardol
© 2022 - 2024 — McMap. All rights reserved.