ChatGPT解决这个技术问题 Extra ChatGPT

如何禁用 RecyclerView 滚动?

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

如何禁用滚动?

如果您不想滚动,使用 RecyclerView 有什么意义?
@CommonsWare,例如,我只想暂时禁用它,而我正在用它的一个孩子制作自定义动画。
啊,好吧,有道理。我可能会在 RecyclerView 上放置一个透明的 View,根据需要在 VISIBLEGONE 之间切换,但您的方法似乎很合理。
@CommonsWare,例如,这就是我需要的。我需要一次在 RecyclerView 中显示一个图像,没有部分可见的图像,只有一个在我的视口中。左侧和右侧都有用户可以导航的箭头。根据当前显示的图像(它们有各种类型),会触发 RecyclerView 之外的一些东西。这是我们客户想要的设计。
IMO 在没有滚动的情况下使用 RecyclerView 有很多用处。也许这只是我,但 RecyclerView 适配器提供了一种简单的方法来动态地为不同类型的视图充气并提高内存效率。我确实有一个自定义的 LinearLayout ,但它需要更多的代码来处理生命周期、viewTypes、项目 ID 和 ListDIffer 适配器......以及自定义动画......等等......而且列表几乎总是伴随着带有标题的标题和菜单等。所有这些项目在更大的可滚动视图中肯定是列表的一部分。

p
philoopher97

为此,您应该覆盖 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;
    }
};

在科特林:

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

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

真正的答案是

recyclerView.setNestedScrollingEnabled(false);

documentation 中的更多信息


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

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

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

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

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

这有点骇人听闻的解决方法,但它有效;您可以在 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 (请参阅其他答案)
s
sergej shafarenka

这对我有用:

  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 相同的警告,请查看此问题的答案:stackoverflow.com/questions/46135249/…
而不是返回 true,因此禁用各种触摸事件,只需 return e.getAction() == MotionEvent.ACTION_MOVE; 而不是 return true;,因此只有滚动/滑动事件被取消。
S
Shailendra Madda

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

冻结:

recyclerView.suppressLayout(true)

解冻:

recyclerView.suppressLayout(false)

这也抑制了 RecycleView 本身的动画以及其他如果仅禁用滚动则不可接受的内容
V
Virat Singh

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

冻结:recyclerView.setLayoutFrozen(true)

解冻:recyclerView.setLayoutFrozen(false)


请注意:当 RecyclerView 冻结时,子视图不会更新。当屏幕上有足够的空间显示所有项目时,我需要禁用滚动。哦……这个任务多好的 Android API……
API 28 中已弃用
y
ypresto
recyclerView.addOnItemTouchListener(new RecyclerView.SimpleOnItemTouchListener() {
        @Override
        public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
            // Stop only scrolling.
            return rv.getScrollState() == RecyclerView.SCROLL_STATE_DRAGGING;
        }
    });


这会在触摸时中止 smoothScrollToPosition() 的滚动。
这对我来说是最好的解决方案,因为它允许以编程方式滚动但避免用户滚动。
这是最好的解决方案!竖起大拇指!
C
Community

创建扩展 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) 。这会帮助你
E
Ekta Bhawsar

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

your_recyclerView.setLayoutFrozen(true);

此方法已弃用。你能推荐另一个吗?
API 28 中已弃用
E
Emad Razavi

有一个简单的答案。

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

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


I
I'm a frog dragon

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

M
Micer

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

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

j
jafarbtech

在 XML 中:-

你可以加

android:nestedScrollingEnabled="false"

在子 RecyclerView 布局 XML 文件中

或者

在 Java 中:-

childRecyclerView.setNestedScrollingEnabled(false);

Java 代码中的 RecyclerView。

使用 ViewCompat (Java):-

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

ViewCompat.setNestedScrollingEnabled(childRecyclerView, false);


太好了,所有的api都应该被加电了。
T
Taig

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

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


A
Aidanvii

扩展 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
K
Karthikeyan

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

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

你应该只添加这一行:

recyclerView.suppressLayout(true)

告诉 RecyclerView 抑制所有布局和滚动调用,直到禁用布局抑制。 refer to docs
v
vovahost

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

y
ypresto

如果您根本不需要 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;
    }
}

a
anas

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

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

很棒的解决方案
P
Pankaj Talaviya

添加

android:descendantFocusability="blocksDescendants"

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


你救了我的命
H
Hasim D

在活动的 onCreate 方法中,您可以简单地执行以下操作:

recyclerView.stopScroll()

它停止滚动。


A
Aladdin

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

A
Atefeh Srch

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

ViewCompat.setNestedScrollingEnabled(recyclerView, false);

现在你的 recyclerview 将与平滑滚动一起工作


T
Tim Jorjev

有一种更直接的方法来禁用滚动(从技术上讲,它更像是拦截滚动事件并在满足条件时结束它),仅使用标准功能。 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 来永久禁用滚动,我想避免它,因为我只需要一些额外的功能,这个解决方案提供了这些功能。
N
Natan Lotério

对于只想阻止用户滚动 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;
                }
            }

b
bwhite

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

            <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”,我使用了一个根据条件更改的布尔变量,以便回收器视图将在禁用和启用之间切换。


G
Guy

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

if (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;
            }
        });
    }

K
Kishan Viramgama

您可以创建一个扩展 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();
    }
}