Как отключить прокрутку RecyclerView?

Я не могу отключить прокрутку в RecyclerView. Я пытался дозвониться rv.setEnabled(false) но я все еще можете прокручивать.

Как отключить прокрутку?

21 ответов


вы должны переопределить layoutmanager вашего recycleview для этого. Таким образом, он отключит только прокрутку, ни одну из других функций. Вы по-прежнему сможете обрабатывать щелчок или любые другие события касания. Например:-

Оригинал:

 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", вы можете временно включить/отключить функцию прокрутки вашего recycle-view.

также:

простое переопределение существующей реализации отключить прокрутку и разрешить щелчок.

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

ответ

recyclerView.setNestedScrollingEnabled(false);

подробнее на документация


Это немного хакерский обходной путь, но он работает; вы можете включить/отключить прокрутку в 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;
      }
  });

для 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">

создать класс, который расширит RecyclerView класс

    public class NonscrollRecylerview extends RecyclerView {

public NonscrollRecylerview(Context context) {
    super(context);
}

public NonscrollRecylerview(Context context, @Nullable AttributeSet attrs) {
    super(context, attrs);
}

public NonscrollRecylerview(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.NonscrollRecylerview 
 ...
 ... >

 ...
 ...

</com.yourpackage.xyz.NonscrollRecylerview >

расширения 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. Это можно сделать с помощью следующего кода:

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.addOnItemTouchListener(new RecyclerView.SimpleOnItemTouchListener() {
        @Override
        public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
            // Stop only scrolling.
            return rv.getScrollState() == RecyclerView.SCROLL_STATE_DRAGGING;
        }
    });


по какой-то причине @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) {
            }
        });

переопределите onTouchEvent () и onInterceptTouchEvent () и верните false, если вам вообще не нужен OnItemTouchListener. Это не отключает OnClickListeners из ViewHolders.

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"

в дочернем XML-файле макета RecyclerView

или

в Java :-

childRecyclerView.setNestedScrollingEnabled(false);

к вашему RecyclerView в коде Java.

Использование ViewCompat (Java): -

childRecyclerView.setNestedScrollingEnabled(false); будет работать только в android_version>21 устройства. для работы во всех устройствах использовать после

ViewCompat.setNestedScrollingEnabled(childRecyclerView, false);


если вы просто отключите только функциональность прокрутки of RecyclerView затем вы можете использовать setLayoutFrozen(true); метод RecyclerView. Но это не может быть отключить событие touch.

your_recyclerView.setLayoutFrozen(true);

я боролся в этом вопросе в течение некоторого часа, Поэтому я хотел бы поделиться своим опытом, Для решения layoutManager это нормально, но если вы хотите восстановить прокрутку, recycler вернется наверх.

лучшее решение до сих пор (для меня, по крайней мере) использует @Zsolt Safrany methode, но добавляет геттер и сеттер, поэтому вам не нужно удалять или добавлять 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 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()

вот как я это сделал с привязкой данных:

            <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" я использовал логическую переменную, которая изменилась на основе условия, чтобы представление recycler переключалось между отключенным и включенным.


столкнулся с фрагментом, который содержит несколько RecycleView, поэтому мне нужна только одна полоса прокрутки вместо одной полосы прокрутки в каждом RecycleView.

поэтому я просто помещаю ScrollView в родительский контейнер, содержащий 2 RecycleViews и использую android:isScrollContainer="false" в RecycleView

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

просто добавьте это в свой recycleview в 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 любого)


существует более простой способ отключить прокрутку, используя только стандартные функции. 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 to true. Таким образом, когда вы пытаетесь прокрутить,RecyclerView автоматически вызывает метод onScrollStateChanged и с isItemSelected значение true, оно остановит немедленно, перед RecyclerView есть шанс, хорошо... прокрутить.

Примечание: Для лучшего удобства использования попробуйте использовать GestureListener вместо OnClickListener в запретить accidental кликов.