如何禁用RecyclerView滚动?

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

我如何禁用滚动?

294817 次浏览

这是一个有点笨拙的方法,但它是有效的;你可以在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

扩展LayoutManager并覆盖__abc1和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);
}
}
}
});
}

另一个替代方法是setLayoutFrozen,但它带有一堆其他副作用。

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

这对我来说很管用:

  recyclerView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
return true;
}
});
由于某些原因@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) {
}
});

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

我特别需要一个标题项,仍然是可点击的,但禁用了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) {


}
});

你应该为此覆盖你的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() = false }

真正的答案是

recyclerView.setNestedScrollingEnabled(false);

更多信息在文档

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

覆盖onTouchEvent()和onInterceptTouchEvent()并返回false,如果你根本不需要OnItemTouchListener。 这不会禁用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;
}
}
我已经为这个问题纠结了好几个小时了, 所以我想分享一下我的经验, 对于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);

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

API 21及以上版本:

无需java代码。 你可以设置android:nestedScrollingEnabled="false" 在xml: < / p >

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

下面是我如何使用数据绑定:

            <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”的地方,我使用了一个基于条件改变的布尔变量,这样回收器视图就会在禁用和启用之间切换。

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

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

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

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

只需将此添加到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">

XML格式:-

你可以添加

android:nestedScrollingEnabled="false"

在子RecyclerView布局XML文件中

在爪哇:-

childRecyclerView.setNestedScrollingEnabled(false);

Java代码中的RecyclerView。

使用ViewCompat (Java):-

childRecyclerView.setNestedScrollingEnabled(false);将只在android_version > 21设备中工作。要在所有设备上工作,请使用以下方法

ViewCompat.setNestedScrollingEnabled(childRecyclerView, false);

添加

android:descendantFocusability="blocksDescendants"

在你的子SrollView或NestedScrollView(和父ListView, recyclerview和gridview任意一个)

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

your_recyclerView.setLayoutFrozen(true);

有一种使用标准功能禁用滚动的更直接的方法(从技术上讲,它更确切地说是拦截滚动事件,并在满足条件时结束它)。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得到机会之前,嗯…滚动。< / p >

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

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

< p >冻结: recyclerView.setLayoutFrozen(true) < / p >

解冻:recyclerView.setLayoutFrozen(false)

您可以在设置适配器之后添加这一行

ViewCompat.setNestedScrollingEnabled(recyclerView, false);

现在您的recyclerview将与平滑滚动工作

答案很简单。

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

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

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

冻结:

recyclerView.suppressLayout(true)

解冻:

recyclerView.suppressLayout(false)

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

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

. 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;
}
});
}

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

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

你只需添加这一行:

recyclerView.suppressLayout(true)

对于谁想要的只是防止用户滚动RecyclerView,而不松动smoothScrollToPosition或任何其他“去位置”;方法,我建议扩展RecyclerView 类,重写 onTouchEvent。这样的:< / p >

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

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

recyclerView.stopScroll()

然后停止滚动。

你可以创建一个不可滚动的回收器视图,它扩展了一个回收器视图类,如下所示:

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

2022年5月

在新版本的android和api中进行了多次尝试后,这种方法对我来说很有效。

Kotlin的新答案

  1. 创建一个名为ScrollDisabledRecyclerViewclass,并放入如下代码:

类ScrollDisabledRecyclerView: RecyclerView {

constructor(context: Context?) : super(context!!)
constructor(context: Context?, @Nullable attrs: AttributeSet?) : super(context!!, attrs)
constructor(context: Context?, @Nullable attrs: AttributeSet?, defStyle: Int) : super(
context!!,
attrs,
defStyle
)


override fun onTouchEvent(e: MotionEvent): Boolean {
return e.action == MotionEvent.ACTION_MOVE
}


override fun onInterceptTouchEvent(e: MotionEvent): Boolean {
return false
}  }
  1. 在你的XML中使用这个类而不是RecyclerView(这个类是从它扩展来的):
 <info.sanaebadi.ScrollDisabledRecyclerView


android:id="@+id/recyclerView"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:clipToPadding="true"
tools:listitem="@layout/multiple_questions_row" />
  1. 最后,用MainActiviy中的按钮处理下一个以前的,如下所示:

注意:我正在使用ViewBinding

binding.buttonNextQuestion.setOnClickListener {
val totalItemCount: Int = binding.recyclerView.adapter!!.itemCount
if (totalItemCount <= 0) return@setOnClickListener
val lastVisibleItemIndex: Int = linearLayoutManager.findLastVisibleItemPosition()
if (lastVisibleItemIndex >= totalItemCount) return@setOnClickListener
linearLayoutManager.smoothScrollToPosition(
binding.recyclerView,
null,
lastVisibleItemIndex + 1
)


}
binding.buttonPreviousQuestion.setOnClickListener {
val firstVisibleItemIndex: Int =
linearLayoutManager.findFirstCompletelyVisibleItemPosition()
if (firstVisibleItemIndex > 0) {
linearLayoutManager.smoothScrollToPosition(
binding.recyclerView,
null,
firstVisibleItemIndex - 1
)
}
}