How to detect if a ListView is fast scrolling

android, fastscroll, listview

Solution

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();
        }

    }
}

Problem

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?

Original source