How to determine if a NestedScrollView is scrolled to the end and is idle?

11,805

Solution 1

Unfortunately, NestedScrollView does not support implementation that dispatches scroll state, because it is totally different kind of Scroll view. Simply it is FrameLayout with Scroller.

Usually, end of a scroll view is reached when ViewCompat.canScrollVertically (scrollView, -1) returns false. For the scroll state you need to subclass NestedScrollView and add your own interface similar to the one of RecyclerView. Your interface method should be called in the following overriden methods:

stopNestedScroll() -> SCROLL_STATE_IDLE

startNestedScroll() -> SCROLL_STATE_DRAGGING

dispatchNestedPreFling() -> SCROLL_STATE_FLINGING

Please don't forget to make the super calls of these methods. If you don't you will break NestedScrollView behavior

Edit:

public class NestedScrollingView extends NestedScrollView {
    private int mState = RecyclerView.SCROLL_STATE_IDLE;

    public interface NestedScrollViewScrollStateListener {
        void onNestedScrollViewStateChanged(int state);
    }


    public void setScrollListener(NestedScrollViewScrollStateListener scrollListener) {
        this.mScrollListener = scrollListener;
    }

    private NestedScrollViewScrollStateListener mScrollListener;

    public NestedScrollingView(Context context) {
        super(context);
    }

    public NestedScrollingView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public NestedScrollingView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    public void stopNestedScroll() {
        super.stopNestedScroll();
        dispatchScrollState(RecyclerView.SCROLL_STATE_IDLE);
    }

    @Override
    public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
        dispatchScrollState(RecyclerView.SCROLL_STATE_DRAGGING);
        return super.onStartNestedScroll(child, target, nestedScrollAxes);
    }


    @Override
    public boolean startNestedScroll(int axes) {
        boolean superScroll = super.startNestedScroll(axes);
        dispatchScrollState(RecyclerView.SCROLL_STATE_DRAGGING);
        return superScroll;
    }


    private void dispatchScrollState(int state) {
        if (mScrollListener != null && mState != state) {
            mScrollListener.onNestedScrollViewStateChanged(state);
            mState = state;
        }
    }

}

Solution 2

as I know the easiest way to do this is to use setOnScrollChangeListener of NestedScrollView :

 NestedScrollView nestedSV = (NestedScrollView) findViewById(R.id.nested_sync_scrollview);

    if (nestedSV != null) {

        nestedSV.setOnScrollChangeListener(new NestedScrollView.OnScrollChangeListener() {
            @Override
            public void onScrollChange(NestedScrollView v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
                String TAG = "nested_sync";
                if (scrollY > oldScrollY) {
                    Log.i(TAG, "Scroll DOWN");
                }
                if (scrollY < oldScrollY) {
                    Log.i(TAG, "Scroll UP");
                }

                if (scrollY == 0) {
                    Log.i(TAG, "TOP SCROLL");
                }

                if (scrollY == (v.getChildAt(0).getMeasuredHeight() - v.getMeasuredHeight())) {
                    Log.i(TAG, "BOTTOM SCROLL");
                    if (!isRecyclerViewWaitingtoLaadData) //check for scroll down
                    {

                        if (!loadedAllItems) {
                            showUnSentData();
                        }
                    }
                }
            }
        });
    }
Share:
11,805
Ofek Ron
Author by

Ofek Ron

What can i say i like to code

Updated on August 07, 2022

Comments

  • Ofek Ron
    Ofek Ron over 1 year

    Tried that :

    NestedScrollView ns =(NestedScrollView) findViewById(R.id.nested_scroll);
            ns.setOnScrollChangeListener(new NestedScrollView.OnScrollChangeListener() {
                @Override
                public void onScrollChange(NestedScrollView v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
    
                }
            });
    

    But got stuck, does anyone have an idea?

    Just to clearify what i want - i want to be able to observe the scroll state (as in addOnScrollListener of RecyclerView) and check only once, when the scrolling has ended (idle), if the user scrolled to the end of the NestedScrollView.