Як відключити прокрутку RecyclerView?


221

Я не можу відключити прокрутку в RecyclerView. Я спробував зателефонувати, rv.setEnabled(false)але все ще можу прокрутити.

Як можна відключити прокрутку?


1
Який сенс використання, RecyclerViewякщо ви не хочете прокручувати?
CommonsWare

16
@CommonsWare, я просто хочу його тимчасово відключити, наприклад, коли я роблю власну анімацію з одним із її дітей.
Zsolt Safrany

1
Ага, добре, це має сенс. Я, мабуть, поставив би прозорий Viewповерх вершини RecyclerView, перемикаючись між VISIBLEі GONEза потреби, але поза манжетою ваш підхід здається розумним.
CommonsWare

Сподіваюсь, це допоможе знайти краще рішення.
Saiteja Parsi

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
Що робити, якщо я хочу відключити прокрутку лише з певного елемента? Значить, ви можете прокрутити його вниз (або вгору), але потім заблокуватися, поки я знову не ввімкну його?
андроїд розробник

2
Це також відключає smoothScrollToPosition
Младен Раконяц

4
ви додаєте об'єкт версії kotlin: LinearLayoutManager (це) {переосмислити забаву canScrollVerically (): Булевий {return false}}
amorenews

149

Справжня відповідь така

recyclerView.setNestedScrollingEnabled(false);

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


39
Це вимкне лише вкладену прокрутку, а не всю прокрутку. Зокрема, якщо ваш RecyclerView знаходиться в ScrollView, але він не заповнює екран, ScrollView не прокручуватиметься (вміст вміщається на екрані), і ви отримаєте інтерфейс прокрутки у своєму RecyclerView (ефект кінця списку при спробі прокрутки, наприклад ), навіть якщо він не прокручується, коли стає більшим. Розширення LayoutManager дійсно виконує цю роботу.
personne3000

3
@ personne3000 Я вважаю, що recilerView.setHasFixedSize (вірно) робить роботу для цього. Редагувати: я також використовую це спільно з 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 ??
Mouaad Abdelghafour AITALI

@pic для нижчих API ви можете використовувати: recilerView.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
Це також відключає натискання елемента?
Jahir Fiquitiva

@JahirFiquitiva Так. "Це порожній RecyclerView.OnItemTouchListener, що краде кожну подію на дотик"
Макс

1
це також вимикає прокрутку батьківського подання
Jemshit Iskenderov

Це також вимкнено натискання предмета
Мухаммад Ріяз

1
Навіщо користуватися хакерським способом (з побічними ефектами), коли є чистий розчин? Просто перекрийте програму LayoutManager (див. Інші відповіді)
personne3000

37

Це працює для мене:

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

11
Це вимикає всі дотики.
Джаред Берроуз

1
Гарний трюк! Щоб знову ввімкнути його, просто встановіть програму onTouchListener onTouch, щоб повернути помилкове
HendraWD

хм, але є попередженняCustom view 'RecyclerView' has setOnTouchListener called on it but does not override performClick
HendraWD

Якщо ви отримуєте те саме попередження, що @HendraWD згадане у коментарі вище, то подивіться на відповіді на це питання: stackoverflow.com/questions/46135249/…
Nicolás Carrasco

1
Замість того, щоб повернути правду, а отже відключити всілякі дотикові події, просто 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 
     ...
     ... 
  />

Дякую! Це все потрібно, коли ми хочемо дозволити перегляду рециркулятора зайняти повний зріст відповідно до вмісту без внутрішнього прокручування. :)
Рахул Растогі

Але якщо цей перегляд рециркулятора знаходиться у вікні прокрутки (egScrollView), тоді менеджер макетів повинен замінити метод canScrollVerically (), повертаючи з нього помилковий.
Рахул Растогі

@RahulRastogi використовувати NestedScrollView замість ScrollView
Ashish

Привіт @AshishMangave, скажіть, будь ласка, як це знову ввімкнути програмно
Shruti

@Shruti, ми не можемо це робити програмно. bcz, ми безпосередньо переосмислюємо метод onMeasure reciklierview. Ви можете спробувати цей recilerView.setNestedScrollingEnabled (помилково). Це допоможе вам
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;
        }
    });


Це перериває прокрутку гладкогоScrollToPosition () при дотику.
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);
          }
        }
      }
    });
  }

1
це також відключає smoothScrollToPosition
Младен Раконяц

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

    }
});

Чомусь вам потрібно клацнути праворуч, не рухаючись, щоб це працювало.
Джаред Берроуз

Хороший момент, @JredBurrows. Це тому, що ми нехтуємо 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);


Відмінно, усі покриті апісом повинні бути включені.
Рікард

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

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

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

2

У Котліні, якщо ви не хочете створити додатковий клас лише для встановлення одного значення, ви можете створити анонімний клас із LayoutManager:

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

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

Замість "істинного" я використав булеву змінну, яка змінилася залежно від умови, щоб перегляд рециркулятора перейшов між вимкненим та ввімкненим.


1

Натрапив на фрагмент, який містить кілька RecycleView, тому мені потрібна лише одна смуга прокрутки замість однієї смуги прокрутки в кожному RecycleView.

Тому я просто помістив ScrollView в батьківський контейнер, який містить 2 RecycleView і використовую 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" />

1

Додайте

android:descendantFocusability="blocksDescendants"

у вашій дитині SrollView або NestedScrollView (і батько ListView, переробник перегляду та перегляд сітки будь-який)


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()не заморожувати прокрутку, це просто зупинити / зупинити прокручування reciklierview.
Фарид

@FARID, так, цей метод просто зупиняє будь-який прокручування, що виконується, і в цьому конкретному випадку це робить кожен раз, коли користувач намагається прокрутити вміст RecyclerView, isItemSelectedвстановленому на true. В іншому випадку вам знадобиться користувальницький RecyclerView, щоб назавжди відключити прокрутку, і я хотів уникнути цього, оскільки мені потрібно було лише трохи додаткової функціональності, яку надає це рішення.
Тім Йорєв

0

Просто додайте це до рециркуляції у 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">

0

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

якщо (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;
            }
        });
    }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.