Last active
November 26, 2025 09:52
-
-
Save cbeyls/b75d730795a4b4c2fcdce554b0b0782a to your computer and use it in GitHub Desktop.
Utility class to enforce a single scroll direction for a RecyclerView or a ViewPager2
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package be.digitalia.samples.utils; | |
| import android.view.MotionEvent; | |
| import androidx.annotation.NonNull; | |
| import androidx.recyclerview.widget.RecyclerView; | |
| import androidx.viewpager2.widget.ViewPager2; | |
| public class RecyclerViewUtils { | |
| @NonNull | |
| public static RecyclerView getRecyclerView(@NonNull ViewPager2 viewPager) { | |
| return (RecyclerView) viewPager.getChildAt(0); | |
| } | |
| public static void enforceSingleScrollDirection(@NonNull RecyclerView recyclerView) { | |
| final SingleScrollDirectionEnforcer enforcer = new SingleScrollDirectionEnforcer(); | |
| recyclerView.addOnItemTouchListener(enforcer); | |
| recyclerView.addOnScrollListener(enforcer); | |
| } | |
| private static class SingleScrollDirectionEnforcer extends RecyclerView.OnScrollListener | |
| implements RecyclerView.OnItemTouchListener { | |
| private int scrollState = RecyclerView.SCROLL_STATE_IDLE; | |
| private int scrollPointerId = -1; | |
| private int initialTouchX; | |
| private int initialTouchY; | |
| private int dx; | |
| private int dy; | |
| @Override | |
| public boolean onInterceptTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e) { | |
| final int action = e.getActionMasked(); | |
| switch (action) { | |
| case MotionEvent.ACTION_DOWN: | |
| scrollPointerId = e.getPointerId(0); | |
| initialTouchX = (int) (e.getX() + 0.5f); | |
| initialTouchY = (int) (e.getY() + 0.5f); | |
| break; | |
| case MotionEvent.ACTION_POINTER_DOWN: | |
| final int actionIndex = e.getActionIndex(); | |
| scrollPointerId = e.getPointerId(actionIndex); | |
| initialTouchX = (int) (e.getX(actionIndex) + 0.5f); | |
| initialTouchY = (int) (e.getY(actionIndex) + 0.5f); | |
| break; | |
| case MotionEvent.ACTION_MOVE: { | |
| final int index = e.findPointerIndex(scrollPointerId); | |
| if (index >= 0 && scrollState != RecyclerView.SCROLL_STATE_DRAGGING) { | |
| final int x = (int) (e.getX(index) + 0.5f); | |
| final int y = (int) (e.getY(index) + 0.5f); | |
| dx = x - initialTouchX; | |
| dy = y - initialTouchY; | |
| } | |
| } | |
| } | |
| return false; | |
| } | |
| @Override | |
| public void onTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e) { | |
| } | |
| @Override | |
| public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) { | |
| } | |
| @Override | |
| public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) { | |
| int oldState = scrollState; | |
| scrollState = newState; | |
| if (oldState == RecyclerView.SCROLL_STATE_IDLE && newState == RecyclerView.SCROLL_STATE_DRAGGING) { | |
| final RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager(); | |
| if (layoutManager != null) { | |
| final boolean canScrollHorizontally = layoutManager.canScrollHorizontally(); | |
| final boolean canScrollVertically = layoutManager.canScrollVertically(); | |
| if (canScrollHorizontally != canScrollVertically) { | |
| if (canScrollHorizontally && Math.abs(dy) > Math.abs(dx)) { | |
| recyclerView.stopScroll(); | |
| } | |
| if (canScrollVertically && Math.abs(dx) > Math.abs(dy)) { | |
| recyclerView.stopScroll(); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } |
Author
@seyoung-hyun Indeed I should probably add an explicit license at the top of the code. My intent was to make it available under the MIT License, allowing the use of it in commercial software. Feel free to use it in such software.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@cbeyls Thanks your code :)
Can I use your code in my commercial app?
Please let me know how I can use the code for commercial distribution.