How to detect if a ListView is fast scrolling

2k Views Asked by At

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?

2

There are 2 best solutions below

4
On

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");
    }
  });
}
7
On

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

    }
}