如何禁用 RecyclerView 滚动?

Posted

技术标签:

【中文标题】如何禁用 RecyclerView 滚动?【英文标题】:How to disable RecyclerView scrolling? 【发布时间】:2015-08-12 09:49:32 【问题描述】:

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

如何禁用滚动?

【问题讨论】:

如果您不想滚动,使用RecyclerView 有什么意义? @CommonsWare,我只是想暂时禁用它,例如,当我正在用它的一个孩子制作自定义动画时。 啊,好吧,有道理。我可能会在RecyclerView 之上放置一个透明的View,根据需要在VISIBLEGONE 之间切换,但你的方法似乎很合理。 希望this 帮助找到更好的解决方案。 @CommonsWare,例如,这就是我所需要的。我需要一次在 RecyclerView 中显示一个图像,没有部分可见的图像,只有一个在我的视口中。左侧和右侧都有用户可以导航的箭头。根据当前显示的图像(它们有各种类型),会触发 RecyclerView 之外的一些东西。这是我们客户想要的设计。 【参考方案1】:

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

原文:

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;
    
;

在 Kotlin 中:

object : LinearLayoutManager(this) override fun canScrollVertically(): Boolean  return false  

【讨论】:

这应该是正确的答案,因为它在允许我点击的同时禁用滚动。 禁用滚动默认 LayoutMangers 应该已经存在,API 的用户不应该这样做。不过感谢您的回答! 如果我只想禁用某个项目的滚动怎么办?这意味着您可以向下(或向上)滚动到它,但随后会被阻止,直到我再次重新启用它? 这也会禁用 smoothScrollToPosition 你添加 kotlin 版本对象 :LinearLayoutManager(this) override fun canScrollVertically(): Boolean return false 【参考方案2】:

真正的答案是

recyclerView.setNestedScrollingEnabled(false);

更多信息documentation

【讨论】:

这只会禁用嵌套滚动,而不是所有滚动。特别是,如果您的 RecyclerView 位于 ScrollView 但未填满屏幕,则 ScrollView 将不会滚动(内容适合屏幕),并且您将在 RecyclerView 中获得滚动 UI(例如尝试滚动时的列表结束效果) 即使它变大时不会滚动。扩展 LayoutManager 确实有效。 @personne3000 我相信 recyclerView.setHasFixedSize(true) 可以做到这一点。编辑:我也将它与 setFillViewport(true) 结合使用,所以我实际上不确定两者中的哪一个修复了它。 这对我来说很好用,但我的“RecyclerView”是在使用“ScrollView”的片段布局内,所以我必须通过“NestedScrollView”更改“ScrollView”,如下所述:***.com/a/38088902/618464 记得使用 NestedScrollView 的完整类名:android.support.v4.widget.NestedScrollView,如此处所述,由 chessdork:***.com/questions/37846245/… 花了 4 个小时试图找出问题所在,这解决了我的问题。在我的例子中,我在 MotionLayout 中使用了一个 recyclerview。我只通过添加motion:touchAnchorId="@id/swipe_area"添加了一个向上拖动的滑动区域。它可以正常工作,但在触摸 recyclerview 的空白区域时也会触发。将motionLayout与其他可滑动元素一起使用时要小心。【参考方案3】:

真正的真正答案是: 对于 API 21 及更高版本:

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

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

【讨论】:

我认为这应该是公认的答案,在课堂上无需任何努力即可禁用滚动,但布局中允许触摸。 21 岁以下的 API 怎么办?? 我接受的答案。 您还应该突出显示与您的答案相关的注意事项。 android:nestedScrollingEnabled="false" 会使onBindViewHolder 一次性调用所有的item,并且item 不会被回收,如果数据很大,这是不好的。 这是最好最直接的答案,同时也避免了不必要的代码污染和无穷无尽的类扩展模式。【参考方案4】:

这有点骇人听闻的解决方法,但它有效;您可以在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 

【讨论】:

这是否也会禁用项目点击? @JahirFiquitiva 是的。 “这是一个空的 RecyclerView.OnItemTouchListener 窃取每个触摸事件” 这也会禁用父视图的滚动 这也会禁用项目点击 当有干净的解决方案时,为什么要使用骇人听闻的解决方法(有副作用)?只需覆盖 LayoutManager(请参阅其他答案)【参考方案5】:

这对我有用:

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

【讨论】:

这会禁用所有触摸。 好招!要重新启用,只需重新设置 onTouchListener 的 onTouch 以返回 false 嗯,不过有警告Custom view 'RecyclerView' has setOnTouchListener called on it but does not override performClick 如果您收到与上述评论中@HendraWD 相同的警告,请查看此问题的答案:***.com/questions/46135249/… 而不是返回 true,因此禁用各种触摸事件,只是 return e.getAction() == MotionEvent.ACTION_MOVE; 而不是 return true; 所以只有滚动/滑动事件被取消。【参考方案6】:

由于 setLayoutFrozen 已弃用,您可以通过使用 suppressLayout 冻结 RecyclerView 来禁用滚动。

冻结:

recyclerView.suppressLayout(true)

解冻:

recyclerView.suppressLayout(false)

【讨论】:

这会抑制 RecycleView 本身的动画以及其他如果仅禁用滚动则不可接受的内容【参考方案7】:

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

冻结: recyclerView.setLayoutFrozen(true)

解冻:recyclerView.setLayoutFrozen(false)

【讨论】:

请注意:当 RecyclerView 冻结时,子视图不会更新。当屏幕上有足够的空间显示所有项目时,我需要禁用滚动。哦……这个任务的 Android API 多棒…… API 28 中已弃用【参考方案8】:

创建扩展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 
     ...
     ... 
  />

【讨论】:

谢谢!当我们希望允许回收器视图根据内容占据全高而不需要内部滚动时,这一切都是必需的。 :) 但是如果这个回收器视图是一个滚动视图(例如 ScrollView),那么布局管理器应该覆盖 canScrollVertically() 方法从它返回 false。 @RahulRastogi 使用 NestedScrollView 而不是 ScrollView 嘿@AshishMangave,请告诉如何以编程方式再次启用它 @Shruti 我们无法以编程方式执行此操作。 bcz 我们直接覆盖 recyclerview 的 onMeasure 方法。你可以试试这个 recyclerView.setNestedScrollingEnabled(false) 。这会帮助你【参考方案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() 的滚动。 这对我来说是最好的解决方案,因为它允许以编程方式滚动但避免用户滚动。 这是最好的解决方案!竖起大拇指!【参考方案10】:

如果您只是禁用RecyclerView仅滚动功能,那么您可以使用RecyclerViewsetLayoutFrozen(true); 方法。但不能禁用触摸事件。

your_recyclerView.setLayoutFrozen(true);

【讨论】:

此方法已弃用。你能推荐另一个吗? API 28 中已弃用【参考方案11】:

有一个非常简单的答案。

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

以上代码禁用 RecyclerView 垂直滚动。

【讨论】:

【参考方案12】:

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

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

【讨论】:

【参考方案13】:

写了一个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()

【讨论】:

【参考方案14】:

在 XML 中:-

你可以添加

android:nestedScrollingEnabled="false"

在子 RecyclerView 布局 XML 文件中

在 Java 中:-

childRecyclerView.setNestedScrollingEnabled(false);

到您的 RecyclerView 中的 Java 代码。

使用 ViewCompat (Java):-

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

ViewCompat.setNestedScrollingEnabled(childRecyclerView, false);

【讨论】:

非常好,所有的api都被覆盖了,应该已经上电了。【参考方案15】:

另一种选择是setLayoutFrozen,但它带来了许多其他副作用。

https://developer.android.com/reference/android/support/v7/widget/RecyclerView.html#setLayoutFrozen(boolean)

【讨论】:

【参考方案16】:

扩展 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);
          
        
      
    );
  

【讨论】:

这也禁用了 smoothScrollToPosition【参考方案17】:

我知道这已经有一个公认的答案,但该解决方案没有考虑到我遇到的用例。

我特别需要一个仍然可点击的标题项,但禁用了 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。为了让触摸感觉自然,在触摸和屏幕上的轻微移动之间会有一些让步。不幸的是,我无法解决这个问题。 谢谢,正是我需要的!【参考方案18】:

你应该只添加这一行:

recyclerView.suppressLayout(true)

【讨论】:

告诉 RecyclerView 抑制所有布局和滚动调用,直到禁用布局抑制。 refer to docs【参考方案19】:

在activity的onCreate方法中,你可以简单的做:

recyclerView.stopScroll()

它会停止滚动。

【讨论】:

【参考方案20】:

出于某种原因,@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) 
            
        );

【讨论】:

【参考方案21】:

如果您根本不需要 OnItemTouchListener,则覆盖 onTouchEvent() 和 onInterceptTouchEvent() 并返回 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;
    

【讨论】:

【参考方案22】:

只需将其添加到 xml 中的回收视图中

 android:nestedScrollingEnabled="false"

喜欢这个

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

【讨论】:

很棒的解决方案【参考方案23】:

添加

android:descendantFocusability="blocksDescendants"

在您的 SrollView 或 NestedScrollView 的子级(以及 ListView、recyclerview 和 gridview 的父级中的任何一个)

【讨论】:

你救了我的命【参考方案24】:

我已经在这个问题上苦苦挣扎了几个小时, 所以我想分享一下我的经验, 对于 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);

【讨论】:

【参考方案25】:

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

ViewCompat.setNestedScrollingEnabled(recyclerView, false);

现在你的 recyclerview 可以平滑滚动了

【讨论】:

【参考方案26】:

有一种更直接的方法来禁用滚动(从技术上讲,它更像是拦截滚动事件并在满足条件时结束它),仅使用标准功能。 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 中的每个项目,因此当您单击一个项目时,它会将isItemSelectedfalse 切换到true。这样,当您尝试滚动时,RecyclerView 将自动调用方法 onScrollStateChanged 并且由于 isItemSelected 设置为 true,它将立即停止,在 RecyclerView 有机会之前,嗯...滚动。

注意:为了更好的可用性,请尝试使用GestureListener 而不是OnClickListener 以防止accidental 点击。

【讨论】:

stopScroll() 不是冻结滚动,它只是停止/停止滚动recyclerview。 @FARID,是的,这个方法只是停止任何正在进行的滚动,在这种特殊情况下,每次当用户尝试滚动 RecyclerView 内容并将isItemSelected 设置为true .否则,您将需要一个自定义 RecyclerView 来永久禁用滚动,我想避免它,因为我只需要一些额外的功能,而这个解决方案提供了这些功能。【参考方案27】:

对于那些只想阻止用户滚动 RecyclerView,而不松动 smoothScrollToPosition 或任何其他“定位”方法的人,我建议宁愿扩展 RecyclerView 类,覆盖 onTouchEvent。像这样:

            public class HardwareButtonsRecyclerView extends RecyclerView 
            
                    public HardwareButtonsRecyclerView(@NonNull Context context) 
                        super(context);
                    
            
                    public HardwareButtonsRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs) 
                        super(context, attrs);
                    
            
                    public HardwareButtonsRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) 
                        super(context, attrs, defStyleAttr);
                    
            
                @Override
                public boolean onTouchEvent(MotionEvent e) 
                    return false;
                
            

【讨论】:

【参考方案28】:

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

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

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

【讨论】:

【参考方案29】:

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

如果(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;
            
        );
    

【讨论】:

【参考方案30】:

您可以创建一个扩展 Recycler View 类的 Non Scrollable Recycler View,如下所示:

import android.content.Context;
import android.util.AttributeSet;
import android.view.ViewGroup;

import androidx.annotation.Nullable;
import androidx.recyclerview.widget.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 widthMeasure, int heightMeasure) 
        int heightMeasureCustom = MeasureSpec.makeMeasureSpec(
                Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
        super.onMeasure(widthMeasure, heightMeasureCustom);
        ViewGroup.LayoutParams params = getLayoutParams();
        params.height = getMeasuredHeight();
    

【讨论】:

以上是关于如何禁用 RecyclerView 滚动?的主要内容,如果未能解决你的问题,请参考以下文章

CoordinatorLayout 中的 RelativeLayout 不滚动

Recyclerview 测量滚动距离以禁用键盘

带有 MotionLayout 的 Android 折叠工具栏 - 当 RecyclerView 为空/不可滚动时禁用运动

如何在 CoordinatorLayout 中禁用 AppBarLayout 的滚动?

Android:为什么TextView中的文本在LinearLayout中滚动而在Recyclerview中滚动

如何使用 AsyncTask 更新 RecyclerView 项目