【问题标题】:Prevent RecyclerView to scroll by hand防止 RecyclerView 手动滚动
【发布时间】:2017-07-13 15:35:06
【问题描述】:

我有一个像专辑这样的项目列表,我通过 RecyclerView 显示它。

此外,每个项目都有一个按钮,可以通过单击向服务器发送一些数据,然后滚动到列表中的下一个位置。

所以我的问题是:

如果调用了smoothScrollToPosition()方法,如何防止RecyclerView手动(手势)滚动但滚动到某个位置?

【问题讨论】:

标签: android android-recyclerview


【解决方案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)
    }
}

【讨论】:

【解决方案2】:

为此,您应该覆盖 RecyclerViewLayoutManager。滚动将被禁用,但您仍然可以处理点击或任何其他触摸事件。例如:

linearLayoutManager = new LinearLayoutManager(context) {
 @Override
 public boolean canScrollVertically() {
  return false;
 }
};
recyclerView.setLayoutManager(linearLayoutManager);

如果你想调用scrollToPosition(),这样做:

linearLayoutManager = new LinearLayoutManager(context) {
 @Override
 public boolean canScrollVertically() {
  return true;
 }
};
recyclerView.setLayoutManager(linearLayoutManager);
linearLayoutManager.scrollToPosition(youePositionInTheAdapter); // where youPositionInTheAdapter is the position you want to scroll to

然后使用第一个代码再次禁用它。

【讨论】:

【解决方案3】:

我很想改进@Ali 提供的答案。有些假设似乎具有误导性。

1:用NestedScrollView 扭曲RecyclerView

<android.support.v4.widget.NestedScrollView
   android:layout_width="match_parent"
   android:layout_height="wrap_content">

   <android.support.v7.widget.RecyclerView
      android:id="@+id/recyclerViewForList"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:nestedScrollingEnabled="false"
      />

</android.support.v4.widget.NestedScrollView>

2:将nestedScrollingEnabled设置为false

有两种选择;在 XML 布局或代码中。

XML 布局

android:nestedScrollingEnabled="false"

代码

recyclerViewForList.setNestedScrollingEnabled(false);

3: 在LinearLayoutManager 中覆盖canScrollVertically

linearLayoutManager = new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false) {
   @Override
   public boolean canScrollVertically() {
      return false;
   }

   //If you want to prevent scrolling horizontally, in my case
   //it was vertically
   @Override
   public boolean canScrollHorizontally() {
      return false;
   }
};

【讨论】:

    【解决方案4】:

    可以通过在列表顶部添加一个透明叠加层来使用触摸事件来快速破解。或者覆盖 dispatchTouchEvent 并改变触摸逻辑

    【讨论】:

      【解决方案5】:

      我通过覆盖 linearLayoutManager 和 RecyclerView ScrollListener 来做到这一点。

      LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false) {
              @Override
              public boolean canScrollHorizontally() {
                  return canScrolling;
              }
      
              @Override
              public boolean canScrollVertically() {
                  return canScrolling;
              }
          };
      
      rvPassengers.addOnScrollListener(new RecyclerView.OnScrollListener() {
              @Override
              public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                  if(newState == RecyclerView.SCROLL_STATE_IDLE){
                      canScrolling = false;
                  }
                  super.onScrollStateChanged(recyclerView, newState);
              }
          });
      

      【讨论】:

        猜你喜欢
        • 2017-04-12
        • 1970-01-01
        • 2016-07-17
        • 1970-01-01
        • 1970-01-01
        • 2021-01-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多