Search code examples
androidandroid-recyclerviewontouchlistenerontouch

RecyclerView that implements OnItemTouchListener with LinearLayoutManager scrolling normally


I am trying to implement a use-case where I need to scroll RecyclerView normally (Android LayoutManager scroll) when the user scrolls with a low velocity and scroll by a certain extra amount when the user scrolls above a certain velocity.

I have implemented a LinearLayoutManager as follows:

public class ScrollLayoutManager extends LinearLayoutManager {
    // constructors

    @Override
    public void onAttachedToWindow(RecyclerView recyclerView) {
        Log.d(TAG, "onAttachedToWindow");
        super.onAttachedToWindow(recyclerView);
        mRecyclerView = recyclerView;
        mRecyclerView.addOnItemTouchListener(new ThresholdDetector());
    }

    private final class ThresholdDetector implements RecyclerView.OnItemTouchListener {
        @Override
        public boolean onInterceptTouchEvent(RecyclerView recyclerView, MotionEvent e) {
            // some calculations
            return true;
        }

        @Override
        public void onTouchEvent(final RecyclerView view, final MotionEvent e) {
            final int action = e.getActionMasked();
            mVelocityTracker.addMovement(e);
            if(action == MotionEvent.ACTION_MOVE) {
                if (thresholdMet) {
                    // Custom scroll
                    mRecyclerView.scrollBy(calculatedAmount)
                } else {
                    // Scroll normally as per Android LinearLayoutManager
                }
            }
        }
    }
}

If threshold is not met, I need Android to handle the scroll, otherwise I am not able to smooth scroll. I tried the following in else (when threshold is not met), but it does not quite work.

                    mRecyclerView.post(new Runnable() {
                        @Override
                        public void run() {
                            mRecyclerView.smoothScrollBy(-(int)getDeltaX(motionEvent), 0);
                        }
                    });

Solution

  • Without seeing the entirety of the ThresholdDetector class, it will be impossible to know for sure what the best solution is. That said, I believe the problem is that you are always returning true from onInterceptTouchEvent().

    In the documentation for the return value of onInterceptTouchEvent():

    Returns: true if this OnItemTouchListener wishes to begin intercepting touch events, false to continue with the current behavior and continue observing future events in the gesture.

    And in the description for onTouchEvent():

    Process a touch event as part of a gesture that was claimed by returning true from a previous call to onInterceptTouchEvent().

    In other words, onTouchEvent() will only be invoked whenever you return true for a given event from onInterceptTouchEvent(). If you want the RecyclerView to perform its default behavior, simply return false from onInterceptTouchEvent() when the scroll velocity is below your threshold.