如何禁用 RecyclerView 滚动?
Posted
技术标签:
【中文标题】如何禁用 RecyclerView 滚动?【英文标题】:How to disable RecyclerView scrolling? 【发布时间】:2015-08-12 09:49:32 【问题描述】:我无法在 RecyclerView
中禁用滚动。我试过打电话给rv.setEnabled(false)
,但我仍然可以滚动。
如何禁用滚动?
【问题讨论】:
如果您不想滚动,使用RecyclerView
有什么意义?
@CommonsWare,我只是想暂时禁用它,例如,当我正在用它的一个孩子制作自定义动画时。
啊,好吧,有道理。我可能会在RecyclerView
之上放置一个透明的View
,根据需要在VISIBLE
和GONE
之间切换,但你的方法似乎很合理。
希望this 帮助找到更好的解决方案。
@CommonsWare,例如,这就是我所需要的。我需要一次在 RecyclerView 中显示一个图像,没有部分可见的图像,只有一个在我的视口中。左侧和右侧都有用户可以导航的箭头。根据当前显示的图像(它们有各种类型),会触发 RecyclerView 之外的一些东西。这是我们客户想要的设计。
【参考方案1】:
为此,您应该覆盖 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;
;
在 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
的仅滚动功能,那么您可以使用RecyclerView
的setLayoutFrozen(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
中的每个项目,因此当您单击一个项目时,它会将isItemSelected
从false
切换到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 不滚动
带有 MotionLayout 的 Android 折叠工具栏 - 当 RecyclerView 为空/不可滚动时禁用运动
如何在 CoordinatorLayout 中禁用 AppBarLayout 的滚动?