如何禁用 RecyclerView 滚动?

How to disable RecyclerView scrolling?

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

如何禁用滚动?

这个解决方法有点老套,但它确实有效;您可以 enable/disable 在 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 并覆盖 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);
          }
        }
      }
    });
  }

另一种选择是 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) {
            }
        });

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

我特别需要一个 header 项目,它仍然可以点击,但禁用了 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。这样它只会禁用滚动,none 其他功能。您仍然可以处理点击或任何其他触摸事件。例如:-

原文:

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”标志,您可以enable/disable临时回收视图的滚动功能。

还有:

简单地覆盖您现有的实现以禁用滚动并允许点击。

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

在 Kotlin 中:

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

真正的答案是

recyclerView.setNestedScrollingEnabled(false);

更多信息见documentation

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() 并且 return 如果您根本不需要 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;
    }
}

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

创建 class 扩展 RecyclerView class

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:

<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

中的回收视图
 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 文件

在Java :-

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 获得机会,嗯...滚动。

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

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

冻结: recyclerView.setLayoutFrozen(true)

解冻: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.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 中,如果您不想创建额外的 class 只是为了设置一个值,您可以从 LayoutManager 创建匿名 class:

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

您应该只添加这一行:

recyclerView.suppressLayout(true)

对于那些只想阻止用户滚动 RecyclerView 而没有松动 smoothScrollToPosition 或任何其他“定位”方法的人,我建议扩展 RecyclerView class, 覆盖 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;
                }
            }

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

recyclerView.stopScroll()

它停止滚动。

您可以创建扩展 Recycler View class 的 Non Scrollable Recyclerview,如下所示:

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 并输入如下代码:

class 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 中使用这个 class 而不是 RecyclerView(这个 class 是从它扩展的):
 <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. 最后,使用[=15]中的按钮处理下一个上一个 =] 像这样:

注意:我正在使用 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
                )
            }
        }