如何以编程方式禁用RecyclerView的滚动

时间:2015-09-27 09:03:51

标签: android android-recyclerview

我的RecyclerView方向为LinearLayoutManager HORIZONTAL。其中的每个项目都可以包含交互元素(包括垂直ScrollView)。有没有办法轻松让RecyclerView忽略用户在不拦截触摸事件给孩子的情况下水平滚动或投掷RecyclerView的任何尝试?

我正在以编程方式控制RecyclerView的滚动,该滚动效果很好,直到用户将其翻转为止。

我尝试过一些非常简单的想法,当我调用smoothScrollToPosition以响应某些事件时,我启用滚动并禁用触摸事件,直到滚动结束。像这样:

  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) {
      }
    }
  }

并像这样使用..

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

其实几乎有效......但是当光滑的滚动处于运动状态时,我仍然可以通过点击屏幕搞砸程序化滚动。很明显,我错过了一些明显的东西,或者有更聪明的方法来做到这一点。

更新此处找到了非RecyclerView解决方案: 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;
  }
}

7 个答案:

答案 0 :(得分:2)

覆盖onInterceptTouchEvent,可以避免点击行为停止。

@Override
public boolean onInterceptTouchEvent(MotionEvent e) {
    return false;
}

答案 1 :(得分:1)

首先,我不认为这是非编程可实现的。

方式1:简单 - 根据触摸禁用其他视图。

如果用户触摸父级,则禁用子视图滚动。反之亦然

执行此操作的代码

recyclerView.setOnTouchListener(new View.OnTouchListener() 
{
   public boolean onTouch(View p_v, MotionEvent p_event) 
    {
       yourChildView.getParent().requestDisallowInterceptTouchEvent(false);
       return false;
    }
});

方式2:因为您的实施中存在小问题。如果你发布它,有人也可以解决它。

答案 2 :(得分:1)

这是水平滚动的解决方案,它允许您关闭滚动,但仍可以通过调用smoothScrollToPosition启用它。它还允许用户与回收者视图中的子视图进行交互。

我发现,其他使临时滚动能够调用smoothScrollToPosition的解决方案具有使用户中断滚动的副作用,即使禁用触摸事件或在recyclerview顶部添加另一个视图也是如此。

关键是要使smoothScrollToPosition正常工作是重写LinearSmoothScroller.calculateDxToMakeVisible并取消对canScrollHorizo​​ntally()的检查

class NoScrollHorizontalLayoutManager(context: Context) : LinearLayoutManager(context, RecyclerView.HORIZONTAL, false) {

    override fun canScrollHorizontally(): Boolean {
        return false
    }

    override fun smoothScrollToPosition(recyclerView: RecyclerView, state: RecyclerView.State?, position: Int) {
        val linearSmoothScroller = ForceHorizontalLinearSmoothScroller(recyclerView.context)
        linearSmoothScroller.targetPosition = position
        startSmoothScroll(linearSmoothScroller)
    }
}



class ForceHorizontalLinearSmoothScroller(context: Context) : LinearSmoothScroller(context) {

    override fun calculateDxToMakeVisible(view: android.view.View, snapPreference: Int): Int {
        val layoutManager = layoutManager
        if (layoutManager == null) {
            return 0
        }
        val params = view.layoutParams as RecyclerView.LayoutParams
        val left = layoutManager.getDecoratedLeft(view) - params.leftMargin
        val right = layoutManager.getDecoratedRight(view) + params.rightMargin
        val start = layoutManager.paddingLeft
        val end = layoutManager.width - layoutManager.paddingRight
        return calculateDtToFit(left, right, start, end, snapPreference)
    }
}

编辑: 我发现在滚动过程中点击回收视图时,用户仍然可以停止滚动。解决方法是覆盖RecycleView.onInterceptTouchEvent并防止在进行平滑滚动时或滚动状态设置为SCROLL_STATE_SETTLING时发生事件。 (为此使用RecycleView.addOnItemTouchListener无效,因为RecycleView停止滚动,然后侦听器在RecyleView.onInterceptTouchEvent中返回true。)

class NoScrollRecyclerView : RecyclerView {
    constructor(context: Context) : super(context)

    constructor(context: Context, attrs: AttributeSet) : super(context, attrs)

    constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle)

    override fun onInterceptTouchEvent(e : MotionEvent) : Boolean {
        if (layoutManager?.isSmoothScrolling() == true || scrollState == SCROLL_STATE_SETTLING) {
            return true
        }
        return super.onInterceptTouchEvent(e)
    }

    @SuppressLint("ClickableViewAccessibility")
    override fun onTouchEvent(e :MotionEvent) : Boolean {
        if (layoutManager?.isSmoothScrolling() == true || scrollState == SCROLL_STATE_SETTLING) {
            return true
        }
        return super.onTouchEvent(e)
    }
}

答案 3 :(得分:0)

recyclerView.setOnTouchListener(new View.OnTouchListener() {
      @Override
      public boolean onTouch(View v, MotionEvent event) {
          return true;
      }
  });

答案 4 :(得分:0)

这会产生连续性效果。

recyclerView.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                int action = event.getAction();
                if (action == MotionEvent.ACTION_DOWN) {
                    recyclerView.smoothScrollToPosition(position);
                    return true;
                } else if (action == MotionEvent.ACTION_UP) {
                    recyclerView.smoothScrollToPosition(position);
                    return true;
                }
                return false;
            }
        });

答案 5 :(得分:-1)

android:nestedScrollingEnabled="false"

在PDF文件的RecyclerView标签中执行上述操作。

答案 6 :(得分:-2)

 recyclerView.setNestedScrollingEnabled(false);

有关详细信息,请参阅documentation