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

221

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

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

Жолт Сафраны
источник
1
Какой смысл использовать, RecyclerViewесли вы не хотите прокрутки?
CommonsWare
16
@CommonsWare, я просто хочу временно отключить его, например, пока я делаю пользовательскую анимацию с одним из его потомков.
Жолт Сафрани
1
Ах, хорошо, это имеет смысл. Возможно, я бы поставил прозрачный Viewповерх RecyclerView, переключаясь между VISIBLEи по GONEмере необходимости, но без маневра ваш подход кажется разумным.
CommonsWare
Надеюсь, что это поможет найти лучшее решение.
Сайтея Парси
1
@ CommonsWare, вот для чего мне это нужно, например. Мне нужно отображать изображения в RecyclerView по одному, без частично видимых изображений, только одно в моем окне просмотра. А слева и справа есть стрелки, по которым пользователь может перемещаться. В зависимости от того, какое изображение отображается в данный момент (они бывают разных типов), некоторые вещи вне RecyclerView запускаются. Это дизайн, который хотят наши клиенты.
Варвара Калинина

Ответы:

368

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

Оригинал:

 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;
 }
};
Саураб Гарг
источник
2
Это должен быть правильный ответ, поскольку он отключает прокрутку, позволяя мне нажимать.
Джаред Берроуз
10
Отключение прокрутки на LayoutMangers по умолчанию уже должно существовать, пользователь API не должен делать это. Спасибо за ответ, хотя!
Мартин О'Ши
1
Что если я хочу отключить прокрутку только для определенного элемента? То есть вы можете прокрутить вниз (или вверх), но затем заблокировать, пока я не включу его снова?
разработчик Android
2
Это также отключает smoothScrollToPosition
Младен Раконджак
4
Вы добавляете объект версии kotlin: LinearLayoutManager (this) {переопределить fun canScrollVertically (): Boolean {return false}}
amorenew
149

Настоящий ответ

recyclerView.setNestedScrollingEnabled(false);

Больше информации в документации

Божич Небойса
источник
39
Это отключит только вложенную прокрутку, а не всю прокрутку. В частности, если ваш RecyclerView находится в ScrollView, но не заполняет экран, ScrollView не будет прокручиваться (содержимое помещается на экране), и вы получите пользовательский интерфейс прокрутки в RecyclerView (эффект конца списка при попытке прокрутки, например, ) хотя он не будет прокручиваться, когда станет больше. Расширение LayoutManager действительно делает свою работу.
personne3000
3
@ personne3000 Я считаю, что recyclerView.setHasFixedSize (true) справится с этой задачей. Редактировать: я также использую это в сочетании с setFillViewport (true), поэтому я не совсем уверен, какой из двух исправляет это.
mDroidd
5
Это прекрасно работает для меня, но мой «RecyclerView» был внутри макета фрагмента с использованием «ScrollView», поэтому мне пришлось изменить «ScrollView» на «NestedScrollView», как описано здесь: stackoverflow.com/a/38088902/618464
educoutinho
1
Не забудьте использовать полное имя класса для NestedScrollView: android.support.v4.widget.NestedScrollViewкак описано здесь, chessdork: stackoverflow.com/questions/37846245/…
gustavoknz
Потратив 4 часа на то, чтобы понять, что не так, это решило мою проблему. В моем случае я использую обзор переработчика внутри MotionLayout. И я добавил область перетаскивания только для перетаскивания, добавив движение: touchAnchorId = "@ id / swipe_area". Он работает нормально, но также срабатывает при касании пустых областей. Будьте осторожны при использовании motionLayout с другими сменными элементами.
О. Кумру
73

РЕАЛЬНО РЕАЛЬНО ответ: для 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">
Милад Фаридния
источник
11
Я думаю, что это должен быть принятый ответ, без каких-либо усилий в классе вы можете отключить прокрутку, и все же касание разрешено в макете.
VipiN Negi
Что за API ниже 21 ??
Муаад Абдельгафур АИТАЛИ
@pic для более низких API вы можете использовать: recyclerView.setNestedScrollingEnabled (false);
Милад Фаридния
1
Принял ответ за меня.
Фернандо Торрес
1
это лучший и самый простой ответ, который также позволяет избежать ненужного загрязнения кода и бесконечных шаблонов расширения классов.
Рафаэль С
52

Это немного хакерский обходной путь, но он работает; Вы можете включить / отключить прокрутку в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 
Жолт Сафраны
источник
8
Это тоже отключает щелчок предмета?
Джахир Фикитива
@JahirFiquitiva Да. «Это пустой RecyclerView.OnItemTouchListener, крадущий каждое сенсорное событие»
Макс
1
это также отключает прокрутку родительского представления
Джемшит Искендеров
Это также отключает щелчок предмета
Мухаммед Рияз
1
Зачем использовать хакерский обходной путь (с побочными эффектами), когда есть чистый раствор? Просто переопределите LayoutManager (см. Другие ответы)
personne3000
37

Это работает для меня:

  recyclerView.setOnTouchListener(new View.OnTouchListener() {
      @Override
      public boolean onTouch(View v, MotionEvent event) {
          return true;
      }
  });
alxgarcia
источник
11
Это отключает все прикосновения.
Джаред Берроуз,
1
Хороший трюк! Чтобы снова включить, просто переустановите onTouch onTouchListener, чтобы он возвращал false
HendraWD
хм, но есть предупреждениеCustom view 'RecyclerView' has setOnTouchListener called on it but does not override performClick
HendraWD
Если вы получаете то же предупреждение, которое @HendraWD упомянул в комментарии выше, взгляните на ответы на этот вопрос: stackoverflow.com/questions/46135249/…
Николас Карраско
1
Вместо того, чтобы возвращать true и, следовательно, отключать все виды сенсорных событий, просто return e.getAction() == MotionEvent.ACTION_MOVE;вместо этого return true;отменяются только события прокрутки / прокрутки.
Toufic Batache
15

Вы можете отключить прокрутку, заморозив RecyclerView.

Заморозить: recyclerView.setLayoutFrozen(true)

Разморозить: recyclerView.setLayoutFrozen(false)

Вират Сингх
источник
1
Обратите внимание: дочерние представления не обновляются, когда RecyclerView заморожен. Мне нужно отключить прокрутку, когда на экране достаточно места, чтобы показать все элементы. Ох ... Какой хороший Android API для этой задачи ...
Александр Нос
1
Устаревший в API 28
OhhhThatVarun
13

Создать класс, который расширяет класс 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 
     ...
     ... 
  />
Ashish
источник
Спасибо! Все это необходимо, когда мы хотим, чтобы представление переработчика занимало полную высоту в соответствии с содержимым без внутренней прокрутки. :)
Рахул Растоги
Но если это представление рециркулятора находится в режиме прокрутки (например, ScrollView), тогда менеджер макета должен переопределить метод canScrollVertically (), возвращая из него значение false.
Рахул Растоги
@RahulRastogi использует NestedScrollView вместо ScrollView
Ашиш
Привет @AshishMangave, расскажи, пожалуйста, как включить его снова программно
Shruti
@ Шрути, мы не можем сделать это программно. bcz, мы напрямую переопределяем метод onMeasure в представлении recyclerview. Вы можете попробовать этот recyclerView.setNestedScrollingEnabled (false). Это поможет вам
Ashish
11

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

your_recyclerView.setLayoutFrozen(true);
Экта Бхавсар
источник
этот метод устарел. можешь предложить другой?
Айаз Пармар
Устаревший в API 28
OhhhThatVarun
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 () при касании.
ypresto
Это лучшее решение для меня, потому что оно позволяет осуществлять прокрутку программно, но позволяет избежать прокрутки пользователем.
Мигель Сесма
7

Существует очень простой ответ.

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

Приведенный выше код отключает вертикальную прокрутку RecyclerView.

Эми Раз
источник
6

Написал котлинскую версию:

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()
Я лягушачий дракон
источник
5

Расширить 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);
          }
        }
      }
    });
  }
Aidanvii
источник
1
это отключает также smoothScrollToPosition
Mladen Rakonjac
5

Я знаю, что на это уже есть принятый ответ, но решение не учитывает вариант использования, с которым я столкнулся.

Мне особенно нужен был элемент заголовка, который можно было нажимать, но при этом отключить механизм прокрутки в 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. Чтобы прикосновение казалось естественным, между прикосновением и легким движением на экране есть некоторое преимущество. К сожалению, я не смог решить эту проблему.
Райан Саймон
Спасибо, именно то, что мне было нужно!
Студент
5

Как setLayoutFrozenне рекомендуется, Вы можете отключить прокрутку, заморозив RecyclerView с помощьюsuppressLayout .

Заморозить:

recyclerView.suppressLayout(true)

Разморозить:

recyclerView.suppressLayout(false)
Шилендра Мадда
источник
4

в XML: -

Можете добавить

android:nestedScrollingEnabled="false"

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

или

в Java: -

childRecyclerView.setNestedScrollingEnabled(false);

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

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

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

ViewCompat.setNestedScrollingEnabled(childRecyclerView, false);

jafarbtech
источник
Отлично, все apis покрыты, должны быть включены.
Рикард
3

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

Переопределите 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;
    }
}
ypresto
источник
3

Вы можете добавить эту строку после настройки вашего адаптера

ViewCompat.setNestedScrollingEnabled(recyclerView, false);

Теперь ваш переработчик будет работать с плавной прокруткой

Атефех Срч
источник
2

Я боролся с этой проблемой в течение часа, поэтому я хотел бы поделиться своим опытом. Для решения layoutManager это хорошо, но если вы захотите включить прокрутку, то вернитесь к началу.

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

В Kotlin, если вы не хотите создавать дополнительный класс только для установки одного значения, вы можете создать анонимный класс из LayoutManager:

recyclerView.layoutManager = object : LinearLayoutManager(context) {
    override fun canScrollVertically(): Boolean = false
}
Micer
источник
1

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

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

bwhite
источник
1

Столкнулся с фрагментом, который содержит несколько 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" />
pk028382
источник
1

Добавить

android:descendantFocusability="blocksDescendants"

в вашем дочернем элементе SrollView или NestedScrollView (и любой родительский элемент ListView, recyclerview и gridview любой)

Панкадж Талавия
источник
1

Существует более простой способ отключить прокрутку (технически это скорее перехват события прокрутки и завершение его при выполнении условия), используя только стандартные функции. 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()это не остановка прокрутки, это просто остановка / остановка прокрутки.
Фарид
@FARID, да, этот метод просто останавливает любую выполняющуюся прокрутку, и в данном конкретном случае он делает это каждый раз, когда пользователь пытается прокрутить содержимое RecyclerView с isItemSelectedустановленным на true. В противном случае вам понадобится пользовательский RecyclerView, чтобы отключить прокрутку навсегда, и я хотел бы избежать этого, поскольку мне потребовалась лишь небольшая дополнительная функциональность, которую предоставляет это решение.
Тим Джоржев
0

Просто добавьте это в свой 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">
ANAS
источник
0

Для остановки прокрутки касанием, но продолжайте прокручивать с помощью команд:

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