Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to disable scrolling of RecyclerView except programmatically

I have a RecyclerView with a LinearLayoutManager in orientation HORIZONTAL. Each item in it can have interactive elements (including vertical ScrollViews). Is there a way to easily make the RecyclerView ignore any attempts by the user to scroll or fling the RecyclerView horizontally without intercepting touch events to the children?

I am programmatically controlling the scroll of the RecyclerView which works great until the user flings it.

I have tried something very simple where the idea is when I call smoothScrollToPosition in response to some event, I enable scrolling and disable touch events until the scroll settles. Like this:

  private class NoScrollHorizontalLayoutManager extends LinearLayoutManager {
    ScrollingTouchInterceptor interceptor = new ScrollingTouchInterceptor();
    protected boolean canScroll;

    public NoScrollHorizontalLayoutManager(Context ctx) {
      super(ctx, LinearLayoutManager.HORIZONTAL, false);
    }

    public RecyclerView.OnItemTouchListener getInterceptor() {
      return interceptor;
    }

    @Override
    public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) {
      canScroll = true;
      super.smoothScrollToPosition(recyclerView, state, position);
    }

    @Override
    public void onScrollStateChanged(int state) {
      super.onScrollStateChanged(state);
      if(state == RecyclerView.SCROLL_STATE_IDLE) {
        canScroll = false;
      }
    }

    @Override
    public boolean canScrollHorizontally() {
      return canScroll;
    }

    public class ScrollingTouchInterceptor implements RecyclerView.OnItemTouchListener {
      @Override
      public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
        return canScrollHorizontally();
      }

      @Override
      public void onTouchEvent(RecyclerView rv, MotionEvent e) {
      }

      @Override
      public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
      }
    }
  }

And use it like this..

NoScrollHorizontalLayoutManager layout = new NoScrollHorizontalLayoutManager(context);
recycler.setLayoutManager(layout);
recycler.addOnItemTouchListener(layout.getInterceptor());

which actually almost works... but I can still screw up the programmatic scroll by tapping the screen when the smooth scroll is in motion. clearly I'm missing something obvious or there is a much smarter way to do this.

UPDATE the non-RecyclerView solution was found here: How do disable paging by swiping with finger in ViewPager but still be able to swipe programmatically?

/**
 * ViewPager that doesn't allow swiping.
 */
public class NonSwipeableViewPager extends ViewPager {

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

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

  @Override
  public boolean onInterceptTouchEvent(MotionEvent event) {
    // Never allow swiping to switch between pages
    return false;
  }

  @Override
  public boolean onTouchEvent(MotionEvent event) {
    // Never allow swiping to switch between pages
    return false;
  }
}
like image 252
danb Avatar asked Sep 11 '25 00:09

danb


2 Answers

Overriding onInterceptTouchEvent you avoid the stop on click behaviour.

@Override
public boolean onInterceptTouchEvent(MotionEvent e) {
    return false;
}
like image 134
Borja Avatar answered Sep 12 '25 14:09

Borja


The answer to disable RecyclerView scrolling by user touch input yet enable scrolling programmatically is to use:

 recyclerView.setOnTouchListener{ _, _ -> true }
 

and then you can use recyclerView.smoothScrollToPosition(position) to scroll programmatically.

like image 31
coditive Avatar answered Sep 12 '25 15:09

coditive