簡體   English   中英

如何禁用 RecyclerView 滾動?

[英]How to disable RecyclerView scrolling?

我無法在RecyclerView中禁用滾動。 我嘗試調用rv.setEnabled(false)但我仍然可以滾動。

如何禁用滾動?

為此,您應該覆蓋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 } }

真正的答案是

recyclerView.setNestedScrollingEnabled(false);

文檔中的更多信息

真正的真正答案是:對於 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">

這有點駭人聽聞的解決方法,但它有效; 您可以在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 

這對我有用:

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

由於不推薦使用setLayoutFrozen ,您可以通過使用suppressLayout凍結 RecyclerView 來禁用滾動。

凍結:

recyclerView.suppressLayout(true)

解凍:

recyclerView.suppressLayout(false)

您可以通過凍結 RecyclerView 來禁用滾動。

凍結: recyclerView.setLayoutFrozen(true)

解凍: recyclerView.setLayoutFrozen(false)

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

創建擴展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 
     ...
     ... 
  />

如果您只禁用RecyclerView滾動功能,那么您可以使用setLayoutFrozen(true); RecyclerView的方法。 但它不能禁用觸摸事件。

your_recyclerView.setLayoutFrozen(true);

有一個簡單的答案。

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

上面的代碼禁用了 RecyclerView 的垂直滾動。

寫了一個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()

在 Kotlin 中,如果您不想為了設置一個值而創建一個額外的類,您可以從 LayoutManager 創建匿名類:

recyclerView.layoutManager = object : LinearLayoutManager(context) {
    override fun canScrollVertically(): Boolean = 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);

另一種選擇是setLayoutFrozen ,但它帶有許多其他副作用。

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

擴展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);
          }
        }
      }
    });
  }

我知道這已經有一個可接受的答案,但該解決方案沒有考慮到我遇到的用例。

我特別需要一個仍然可以點擊的標題項,但禁用了 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) {

    }
});

你應該只添加這一行:

recyclerView.suppressLayout(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) {
            }
        });

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

只需將其添加到 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">

添加

android:descendantFocusability="blocksDescendants"

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

在活動的 onCreate 方法中,您可以簡單地執行以下操作:

recyclerView.stopScroll()

它停止滾動。

我已經在這個問題上苦苦掙扎了幾個小時,所以我想分享我的經驗,對於 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有一個叫做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點擊。

您可以在設置適配器后添加此行

ViewCompat.setNestedScrollingEnabled(recyclerView, false);

現在你的 recyclerview 將與平滑滾動一起工作

對於只想阻止用戶滾動 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;
                }
            }

以下是我使用數據綁定的方法:

            <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”,我使用了一個根據條件更改的布爾變量,以便回收器視圖將在禁用和啟用之間切換。

對於通過觸摸停止滾動但通過命令繼續滾動:

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

您可以創建一個擴展 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();
    }
}

遇到一個包含多個 RecycleView 的片段,所以我只需要一個滾動條而不是每個 RecycleView 中的一個滾動條。

所以我只是將 ScrollView 放在包含 2 個 RecycleViews 的父容器中,並在 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" />

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM