如何禁用RecyclerView滚动?


221

我无法禁止在中滚动RecyclerView。我尝试打电话,rv.setEnabled(false)但仍然可以滚动。

如何禁用滚动?


1
RecyclerView如果不想滚动,使用的目的是什么?
CommonsWare,2015年

16
@CommonsWare,例如,我只是想暂时禁用它,而与此同时我正在对其子级之一进行自定义动画。
Zsolt Safrany,2015年

1
嗯,好,那是有道理的。我可能已经ViewRecyclerViewVISIBLE以及GONE根据需要切换之间放置了透明的内容,但是袖手旁观,您的方法似乎是合理的。
CommonsWare,2015年

希望有助于找到更好的解决方案。
Saiteja Parsi

1
@CommonsWare,例如,这就是我需要的东西。我需要一次在RecyclerView中显示一幅图像,没有部分可见的图像,而在我的视口中只能显示一幅。左右两侧都有可供用户浏览的箭头。根据当前显示的图像(它们是各种类型),会触发RecyclerView之外的某些事件。这是我们客户想要的设计。
Varvara Kalinina

Answers:


368

为此,您应该覆盖您的recycleview的layoutmanager。这样,它将仅禁用滚动,而没有其他功能。您仍然可以处理点击或任何其他触摸事件。例如:-

原版的:

 public class CustomGridLayoutManager extends LinearLayoutManager {
 private boolean isScrollEnabled = true;

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

 public void setScrollEnabled(boolean flag) {
  this.isScrollEnabled = flag;
 }

 @Override
 public boolean canScrollVertically() {
  //Similarly you can customize "canScrollHorizontally()" for managing horizontal scroll
  return isScrollEnabled && super.canScrollVertically();
 }
}

在这里,使用“ isScrollEnabled”标志可以临时启用/禁用回收视图的滚动功能。

也:

简单覆盖现有的实现即可禁用滚动并允许单击。

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

2
这应该是正确的答案,因为它在允许我单击的同时禁用了滚动。
贾里德·伯罗斯

10
禁用默认LayoutMangers上的滚动应该已经存在,API的用户不必这样做。谢谢你的回答!
Martin O'Shea

1
如果我只想禁用某个项目的滚动怎么办?意思是您可以向下(或向上)滚动到它,但是随后被阻止,直到我再次重新启用它?
Android开发人员

2
这也会禁用smoothScrollToPosition
Mladen Rakonjac

4
您添加kotlin版本对象:LinearLayoutManager(this){覆盖fun canScrollVertically():布尔值{return false}}
amorenew

149

真正的答案是

recyclerView.setNestedScrollingEnabled(false);

文档中的更多信息


39
这将仅禁用嵌套滚动,而不是全部滚动。特别是,如果您的RecyclerView在ScrollView中但没有填满屏幕,则ScrollView将不会滚动(内容适合屏幕),并且您将在RecyclerView中获得滚动UI(例如,尝试滚动时列表结束效果) ),即使它变大也不会滚动。扩展LayoutManager确实可以完成工作。
personne3000

3
@ personne3000我相信recyclerView.setHasFixedSize(true)可以做到这一点。编辑:我也将其与setFillViewport(true)结合使用,因此我实际上不确定两个解决方案之一。
mDroidd

5
这对我来说很好用,但是我的“ RecyclerView”位于使用“ ScrollView”的Fragment布局中,因此我不得不通过“ NestedScrollView”更改“ ScrollView”,如下所述:stackoverflow.com/a/38088902/618464
educoutinho

1
记得使用完整的类名NestedScrollViewandroid.support.v4.widget.NestedScrollView,如此处所述,由chessdork编写:stackoverflow.com/questions/37846245/…–
gustavoknz

花了4个小时试图找出问题所在后,这解决了我的问题。就我而言,我在MotionLayout中使用了recyclerview。而且我只添加了滑动区域以通过添加motion:touchAnchorId =“ @ id / swipe_area”向上拖动。它正常工作,但在接触到recyclerview的空白区域时也会触发。当将motionLayout与其他可滑动元素一起使用时,请当心。
O. Kumru,

73

REAL REAL的答案是: 对于API 21和更高版本:

无需Java代码。 您可以android:nestedScrollingEnabled="false" 在xml中设置 :

<android.support.v7.widget.RecyclerView
     android:id="@+id/recycler"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:clipToPadding="true"
     android:nestedScrollingEnabled="false"
     tools:listitem="@layout/adapter_favorite_place">

11
我认为这应该是可以接受的答案,在课堂上无需付出任何努力,您就可以禁用滚动,但布局中仍允许触摸。
VipiN Negi '18

对于21以下的API呢?
Mouaad Abdelghafour AITALI

@pic可以使用较低的API:recyclerView.setNestedScrollingEnabled(false);
Milad Faridnia

1
为我接受了答案。
费尔南多·托雷斯

1
这是最好,最直接的答案,这也避免了不必要的代码污染和无尽的类扩展模式。
拉斐尔C

52

这有点棘手,但可行。您可以在中启用/禁用滚动RecyclerView

这是一次空洞的RecyclerView.OnItemTouchListener窃取,每一次触摸事件都会使目标无效RecyclerView

public class RecyclerViewDisabler implements RecyclerView.OnItemTouchListener {

    @Override
    public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
        return true;
    }

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

    }

    @Override
    public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {

    }
}

使用它:

RecyclerView rv = ...
RecyclerView.OnItemTouchListener disabler = new RecyclerViewDisabler();

rv.addOnItemTouchListener(disabler);        // disables scolling
// do stuff while scrolling is disabled
rv.removeOnItemTouchListener(disabler);     // scrolling is enabled again 

8
这是否也会禁用项目单击?
Jahir Fiquitiva 2015年

@JahirFiquitiva是的。“这是一个空的RecyclerView.OnItemTouchListener,它窃取了每个触摸事件”
最多

1
这也会同时禁用父视图的滚动
Jemshit Iskenderov

这也会禁用项目点击
Muhammad Riyaz

1
有干净的解决方案时,为什么要使用骇人的解决方法(具有副作用)?只需覆盖LayoutManager(请参阅其他答案)
personne3000

37

这对我有用:

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

11
这将禁用所有触摸。
贾里德·伯罗斯

1
好招!要重新启用,只需将onTouchListener的onTouch设置为false即可
HendraWD

嗯,但是有个警告Custom view 'RecyclerView' has setOnTouchListener called on it but does not override performClick
HendraWD

如果您收到相同的警告@HendraWD在评论中提及了上述再看看这个问题的答案:stackoverflow.com/questions/46135249/...
尼古拉斯卡拉斯科

1
而不是返回true,因此会禁用各种触摸事件,return e.getAction() == MotionEvent.ACTION_MOVE;return true;不仅仅是取消滚动/滑动事件。
Toufic Batache

15

您可以通过冻结RecyclerView来禁用滚动。

冻结: recyclerView.setLayoutFrozen(true)

要解冻: recyclerView.setLayoutFrozen(false)


1
注意:冻结RecyclerView时不会更新子视图。当屏幕上有足够的空间来显示所有项目时,我需要禁用滚动。哦...这个任务的Android API真不错...
Oleksandr Nos

1
API 28中已弃用
OhhhThatVarun

13

创建扩展RecyclerView类的类

public class NonScrollRecyclerView extends RecyclerView {

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

    public NonScrollRecyclerView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public NonScrollRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int heightMeasureSpec_custom = MeasureSpec.makeMeasureSpec(
                Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
        super.onMeasure(widthMeasureSpec, heightMeasureSpec_custom);
        ViewGroup.LayoutParams params = getLayoutParams();
        params.height = getMeasuredHeight();
    }
}

这将禁用滚动事件,但不会禁用单击事件

在XML中使用它执行以下操作:

  <com.yourpackage.xyx.NonScrollRecyclerView 
     ...
     ... 
  />

谢谢!当我们要允许回收者视图根据内容完全显示高度而无需内部滚动时,就需要所有这些。:)
Rahul Rastogi

但是,如果此回收者视图在滚动视图中(例如,ScrollView),则布局管理器应重写canScrollVertically()方法,从中返回false。
Rahul Rastogi '18

@RahulRastogi使用NestedScrollView而不是ScrollView
Ashish

嘿@AshishMangave,请告诉我们如何以编程方式再次启用它
Shruti

@Shruti我们无法以编程方式执行此操作。顺便说一句,我们直接重写recyclerview的onMeasure方法。您可以尝试使用此recyclerView.setNestedScrollingEnabled(false)。这将对您有所帮助
Ashish


9
recyclerView.addOnItemTouchListener(new RecyclerView.SimpleOnItemTouchListener() {
        @Override
        public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
            // Stop only scrolling.
            return rv.getScrollState() == RecyclerView.SCROLL_STATE_DRAGGING;
        }
    });


触摸时,这将中止smoothScrollToPosition()的滚动。
ypresto

这对我来说是最好的解决方案,因为它允许以编程方式滚动,但避免用户滚动。
米格尔·塞斯玛

7

有一个非常简单的答案。

LinearLayoutManager lm = new LinearLayoutManager(getContext()) {
                @Override
                public boolean canScrollVertically() {
                    return false;
                }
            };

上面的代码禁用了RecyclerView垂直滚动。



6

编写了Kotlin版本:

class NoScrollLinearLayoutManager(context: Context?) : LinearLayoutManager(context) {
    private var scrollable = true

    fun enableScrolling() {
        scrollable = true
    }

    fun disableScrolling() {
        scrollable = false
    }

    override fun canScrollVertically() =
            super.canScrollVertically() && scrollable


    override fun canScrollHorizontally() =
            super.canScrollVertically()

 && scrollable
}

用法:

recyclerView.layoutManager = NoScrollLinearLayoutManager(context)
(recyclerView.layoutManager as NoScrollLinearLayoutManager).disableScrolling()

5

扩展LayoutManager和覆盖canScrollHorizontally()canScrollVertically()禁用滚动。

请注意,在开头插入项目不会自动回滚到开头,要解决此问题,请执行以下操作:

  private void clampRecyclerViewScroll(final RecyclerView recyclerView)
  {
    recyclerView.getAdapter().registerAdapterDataObserver(new RecyclerView.AdapterDataObserver()
    {
      @Override
      public void onItemRangeInserted(int positionStart, int itemCount)
      {
        super.onItemRangeInserted(positionStart, itemCount);
        // maintain scroll position at top
        if (positionStart == 0)
        {
          RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
          if (layoutManager instanceof GridLayoutManager)
          {
            ((GridLayoutManager) layoutManager).scrollToPositionWithOffset(0, 0);
          }else if(layoutManager instanceof LinearLayoutManager)
          {
            ((LinearLayoutManager) layoutManager).scrollToPositionWithOffset(0, 0);
          }
        }
      }
    });
  }

1
这也会禁用smoothScrollToPosition
Mladen Rakonjac

5

我知道这已经有了一个可以接受的答案,但是该解决方案并未考虑到我遇到的一个用例。

我特别需要一个仍可单击的标题项,但禁用了RecyclerView的滚动机制。这可以通过以下代码完成:

recyclerView.addOnItemTouchListener(new RecyclerView.OnItemTouchListener() {
                            @Override
     public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
         return e.getAction() == MotionEvent.ACTION_MOVE;
     }

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

     }

     @Override
     public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {

    }
});

出于某种原因,您必须单击右键,而无需移动它即可正常工作。
贾里德·伯罗斯

好点,@ JaredBurrows。这是因为我们无视ACTION_MOVE。为了使触摸感觉自然,在触摸和屏幕轻微移动之间要有所取舍。不幸的是,我无法解决该问题。
瑞安·西蒙

谢谢,正是我所需要的!
学生,

5

setLayoutFrozen不建议使用的那样,您可以通过使用冻结冻结RecyclerView来禁用滚动suppressLayout

冻结:

recyclerView.suppressLayout(true)

要解冻:

recyclerView.suppressLayout(false)

4

在XML中:

你可以加

android:nestedScrollingEnabled="false"

在子RecyclerView布局XML文件中

要么

用Java:-

childRecyclerView.setNestedScrollingEnabled(false);

以Java代码访问您的RecyclerView。

使用ViewCompat(Java):

childRecyclerView.setNestedScrollingEnabled(false);仅适用于android_version> 21设备。要在所有设备上工作,请使用以下命令

ViewCompat.setNestedScrollingEnabled(childRecyclerView, false);


Excelent,涵盖了所有api,应通电。
Ricard

3

由于某种原因,@ Alejandro Gracia答案仅在几秒钟后才开始起作用。我找到了一种可立即阻止RecyclerView的解决方案:

recyclerView.addOnItemTouchListener(new RecyclerView.OnItemTouchListener() {
            @Override
            public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
                return true;
            }
            @Override
            public void onTouchEvent(RecyclerView rv, MotionEvent e) {
            }
            @Override
            public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
            }
        });

3

重写onTouchEvent()和onInterceptTouchEvent(),如果根本不需要OnItemTouchListener,则返回false。这不会禁用ViewHolders的OnClickListeners。

public class ScrollDisabledRecyclerView extends RecyclerView {
    public ScrollDisabledRecyclerView(Context context) {
        super(context);
    }

    public ScrollDisabledRecyclerView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public ScrollDisabledRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

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

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

3

您可以在设置适配器后添加此行

ViewCompat.setNestedScrollingEnabled(recyclerView, false);

现在您的recyclerview将可以平滑滚动


2

我在这个问题上苦苦挣扎了一个小时,所以我想分享一下我的经验,对于layoutManager解决方案来说很好,但是如果您要重新启用滚动回收站,则会回到顶部。

到目前为止(至少对我而言)最好的解决方案是使用@Zsolt Safrany方法,但添加getter和setter,这样您就不必删除或添加OnItemTouchListener。

如下

public class RecyclerViewDisabler implements RecyclerView.OnItemTouchListener {

    boolean isEnable = true;

    public RecyclerViewDisabler(boolean isEnable) {
        this.isEnable = isEnable;
    }

    public boolean isEnable() {
        return isEnable;
    }

    public void setEnable(boolean enable) {
        isEnable = enable;
    }

    @Override
    public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
        return !isEnable;
    }

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

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

用法

RecyclerViewDisabler disabler = new RecyclerViewDisabler(true);
feedsRecycler.addOnItemTouchListener(disabler);

// TO ENABLE/DISABLE JUST USE THIS
disabler.setEnable(enable);

2

在Kotlin中,如果您不想仅为了设置一个值而创建额外的类,则可以从LayoutManager创建匿名类:

recyclerView.layoutManager = object : LinearLayoutManager(context) {
    override fun canScrollVertically(): Boolean = false
}

1

这是我使用数据绑定的方法:

            <android.support.v7.widget.RecyclerView
                android:id="@+id/recycler_view"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:clipChildren="false"
                android:onTouch="@{(v,e) -> true}"/>

我使用一个布尔变量代替“ true”,该变量根据条件而改变,以便回收者视图可以在禁用和启用之间切换。


1

遇到了一个包含多个RecycleView的片段,因此我只需要一个滚动条,而不是每个RecycleView中的一个滚动条。

所以我只是将ScrollView放入包含2个RecycleViews的父容器中,并android:isScrollContainer="false"在RecycleView中使用

<android.support.v7.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layoutManager="LinearLayoutManager"
    android:isScrollContainer="false" />


1

有一种更简单的方法来禁用滚动(从技术上讲,它是拦截滚动事件并在满足条件时结束滚动事件),仅使用标准功能即可。RecyclerView具有名为的方法addOnScrollListener(OnScrollListener listener),仅使用此方法就可以阻止其滚动,如下所示:

recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
    @Override
    public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
        super.onScrollStateChanged(recyclerView, newState);
        if (viewModel.isItemSelected) {
            recyclerView.stopScroll();
        }
    }
});

用例: 假设您要在单击其中一个项目时禁用滚动,RecyclerView以便可以对其执行一些操作,而不会因意外滚动到另一个项目而分心,当您完成操作后,只需单击再次启用该项目以滚动。为此,您需要附加OnClickListener到其中的每个项目RecyclerView,因此当您单击某个项目时,它将isItemSelected从切换falsetrue。这样,当您尝试滚动时,RecyclerView将自动调用method,onScrollStateChanged并且由于isItemSelected设置为true,它将在RecyclerView有机会滚动。

注意:为提高可用性,请尝试使用GestureListener而不是OnClickListener防止accidental点击。


stopScroll()并不是要冻结滚动,而只是停止/停止滚动recyclerview。
法里德

@FARID,是的,此方法只是停止正在进行的任何滚动,在这种特殊情况下,每次用户尝试滚动isItemSelected设置为的RecyclerView内容时,它都会这样做true。否则,您将需要一个自定义的RecyclerView来永久禁用滚动,而我想避免这样做,因为我只需要此解决方案提供的一些额外功能。
Tim Jorjev

0

只需将其添加到XML的recycleview中

 android:nestedScrollingEnabled="false"

像这样

<android.support.v7.widget.RecyclerView
                    android:background="#ffffff"
                    android:id="@+id/myrecycle"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:nestedScrollingEnabled="false">

0

要通过触摸停止滚动,但继续通过命令滚动:

如果(appTopBarMessagesRV == null){appTopBarMessagesRV = findViewById(R.id.mainBarScrollMessagesRV);

        appTopBarMessagesRV.addOnItemTouchListener(new RecyclerView.SimpleOnItemTouchListener() {
            @Override
            public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {

                if ( rv.getScrollState() == RecyclerView.SCROLL_STATE_DRAGGING)
                {
                     // Stop  scrolling by touch

                    return false;
                }
                return  true;
            }
        });
    }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.