ChatGPT解决这个技术问题 Extra ChatGPT

How to disable RecyclerView scrolling?

I cannot disable scrolling in the RecyclerView. I tried calling rv.setEnabled(false) but I can still scroll.

How can I disable scrolling?

What is the point of using RecyclerView if you do not want scrolling?
@CommonsWare, I just want to disable it temporarily, for instance, while I'm doing a custom animation with one of its children.
Ah, OK, that makes sense. I'd've probably put a transparent View over top of the RecyclerView, toggling between VISIBLE and GONE as needed, but off the cuff your approach seems reasonable.
@CommonsWare, here's what I need it for, for example. I need to display images in RecyclerView one at a time, without partially visible images, only one in my viewport. And there are arrows on the left and on the right which user can navigate with. Depending on what image is currently displayed (they are of various types), some things outside RecyclerView are triggered. It's the design our customers want.
IMO there is a lot of use in using a RecyclerView without scroll. Maybe this is just me but the RecyclerView adapter offers a simple way to dynamically inflate different types of views with memory efficiency. I do have a custom LinearLayout , but it requires a lot more code to handle lifecycles, viewTypes, items ID's and ListDIffer adapters... with custom animations...etc... Also a list is almost always accompanied by a header with titles and menus, etc.. and all of those items are most certainly part of the List in a bigger scrollable view.

p
philoopher97

You should override the layoutManager of your recycleView for this. This way it will only disable scrolling, none of the other functionalities. You will still be able to handle click or any other touch events. For example:-

Original:

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

Here using "isScrollEnabled" flag you can enable/disable scrolling functionality of your recycle-view temporarily.

Also:

Simple override your existing implementation to disable scrolling and allow clicking.

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

In Kotlin:

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

This should be the correct answer as it disables scrolling while allowing me to click.
Disabling scrolling on the default LayoutMangers should already exist, a user of the API should not have to do this. Thanks for the answer though!
What if I want to disable scrolling only from a certain item ? Meaning you can scroll down (or up) to it, but then get blocked, till I re-enable it again?
This also disables smoothScrollToPosition
you add kotlin version object :LinearLayoutManager(this){ override fun canScrollVertically(): Boolean { return false } }
B
Bozic Nebojsa

The real answer is

recyclerView.setNestedScrollingEnabled(false);

More info in documentation


This will only disable nested scrolling, not all scrolling. In particular, if your RecyclerView is in a ScrollView but does not fill the screen, the ScrollView will not scroll (content fits in screen), and you will get the scroll UI in your RecyclerView (end of list effect when trying to scroll for example) even though it will not scroll when it gets bigger. Extending the LayoutManager really does the job.
@personne3000 I believe recyclerView.setHasFixedSize(true) does the job for that. Edit: I also use this in conjuction with setFillViewport(true) so I'm not actually sure which one of the two fixes it.
This works fine for me, but my "RecyclerView" was inside a Fragment layout using "ScrollView", so I had to change the "ScrollView" by "NestedScrollView", as described here: stackoverflow.com/a/38088902/618464
Remember to use the full class name for NestedScrollView: android.support.v4.widget.NestedScrollView, as described here, by chessdork: stackoverflow.com/questions/37846245/…
After spending 4 hours to trying to figure out what is wrong this solved my issue. In my case I am using a recyclerview inside of a MotionLayout. And I only added a swipe area to drag up by adding motion:touchAnchorId="@id/swipe_area". It works normally but also triggered when touched recyclerview's empty areas. Be careful when using motionLayout with other swipable elements.
M
Milad Faridnia

The REAL REAL answer is: For API 21 and above:

No java code needed. You can set android:nestedScrollingEnabled="false" in 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">

I think this should be the accepted answer, without any effort in class you can disable the scrolling and yet the touch is allowed in the layout.
What for APIs below 21 ??
Accepted answer for me.
You should also highlight the caveats associated with your answer. android:nestedScrollingEnabled="false" will make the onBindViewHolder called for all the items at once and the items will not get recycled which is not good if the data is large.
this is the best and most straightforward answer, which also avoids unnecessary code pollution, and endless class extending patterns.
w
worked

This a bit hackish workaround but it works; you can enable/disable scrolling in the RecyclerView.

This is an empty RecyclerView.OnItemTouchListener stealing every touch event thus disabling the target 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) {

    }
}

Using it:

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 

Does this disable the item clicking too?
@JahirFiquitiva Yes. "This is an empty RecyclerView.OnItemTouchListener stealing every touch event"
this also disables parent view's scroll as well
This disables item click too
Why use a hackish workaround (with side effects) when there is a clean solution ? Just override the LayoutManager (see other answers)
s
sergej shafarenka

This works for me:

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

This disables all touch.
Nice trick! To re-enable again, just re-set the onTouchListener's onTouch to return false
uhm, but there is a warning Custom view 'RecyclerView' has setOnTouchListener called on it but does not override performClick
If you are receiving the same warning that @HendraWD mentioned in the comment above then take a look at this question's answers: stackoverflow.com/questions/46135249/…
Instead of returning true, and therefore disabling all sorts of touch events, just return e.getAction() == MotionEvent.ACTION_MOVE; instead of return true; so only scroll/swipe events get cancelled.
S
Shailendra Madda

As setLayoutFrozen is deprecated, You can disable scrolling by freezing your RecyclerView by using suppressLayout.

To freeze:

recyclerView.suppressLayout(true)

To unfreeze:

recyclerView.suppressLayout(false)

This suppress animation of RecycleView itself as well and other stuff which is not acceptable if taking about only disable scroll
V
Virat Singh

You can disable scrolling by freezing your RecyclerView.

To freeze: recyclerView.setLayoutFrozen(true)

To unfreeze: recyclerView.setLayoutFrozen(false)


Take a note: child views are not updated when RecyclerView is frozen. I need to disable scroll when there's enough room on the screen to show all items. Oh... What a nice Android API for this task...
Deprecated in API 28
y
ypresto
recyclerView.addOnItemTouchListener(new RecyclerView.SimpleOnItemTouchListener() {
        @Override
        public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
            // Stop only scrolling.
            return rv.getScrollState() == RecyclerView.SCROLL_STATE_DRAGGING;
        }
    });


This aborts scrolling of smoothScrollToPosition() when touched.
This is the best solution for me because it allows scrolling programatically but avoids user scrolling.
This is the best solution! thumbs up!
C
Community

Create class which extend 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();
    }
}

This will disable the scroll event, but not the click events

Use this in your XML do the following:

  <com.yourpackage.xyx.NonScrollRecyclerView 
     ...
     ... 
  />

Thanks! This all is needed when we want to allow recycler view to take full height according to contents without internal scrolling. :)
But if this recycler view is in a scrolling view (e.g.ScrollView) then the layout manager should override canScrollVertically() method returning false from it.
@RahulRastogi use NestedScrollView instead of ScrollView
Hey @AshishMangave , Please tell how to enable it again programmatically
@Shruti we can’t do this programatically. bcz we directly override recyclerview ’s onMeasure method.you can try for this recyclerView.setNestedScrollingEnabled(false) .this will help you
E
Ekta Bhawsar

If you just disable only scroll functionality of RecyclerView then you can use setLayoutFrozen(true); method of RecyclerView. But it can not be disable touch event.

your_recyclerView.setLayoutFrozen(true);

this method is deprecated. can you suggest another one?
Deprecated in API 28
E
Emad Razavi

There is a simple answer.

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

The above code disables RecyclerView's verticall scrolling.


I
I'm a frog dragon

Wrote a kotlin version:

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
}

usage:

recyclerView.layoutManager = NoScrollLinearLayoutManager(context)
(recyclerView.layoutManager as NoScrollLinearLayoutManager).disableScrolling()

M
Micer

In Kotlin, if you don't want to create an extra class just for setting one value, you can create anonymous class from LayoutManager:

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

j
jafarbtech

in XML :-

You can add

android:nestedScrollingEnabled="false"

in the child RecyclerView layout XML file

or

in Java :-

childRecyclerView.setNestedScrollingEnabled(false);

to your RecyclerView in Java code.

Using ViewCompat (Java) :-

childRecyclerView.setNestedScrollingEnabled(false); will work only in android_version>21 devices. to work in all devices use the following

ViewCompat.setNestedScrollingEnabled(childRecyclerView, false);


Excelent, all apis covered, should be powered up.
T
Taig

Another alternative is setLayoutFrozen, but it comes with a bunch of other side effects.

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


A
Aidanvii

Extend the LayoutManager and override canScrollHorizontally()and canScrollVertically() to disable scrolling.

Be aware that inserting items at the beginning will not automatically scroll back to the beginning, to get around this do something like:

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

this disables also smoothScrollToPosition
K
Karthikeyan

I know this already has an accepted answer, but the solution doesn't take into account a use-case that I came across.

I specifically needed a header item that was still clickable, yet disabled the scrolling mechanism of the RecyclerView. This can be accomplished with the following code:

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

    }
});

For some reason, you have to click just right, without moving for this to work.
Good point, @JaredBurrows. This is because we're disregarding ACTION_MOVE. For touch to feel natural, there's some give between touching, and slight movement on the screen. Unfortunately, I wasn't able to solve for that problem.
Thanks, exactly what I needed!
M
Majid Sadeghi

You should just add this line:

recyclerView.suppressLayout(true)

Tells the RecyclerView to suppress all layout and scroll calls until layout suppression is disabled. refer to docs
v
vovahost

For some reason @Alejandro Gracia answer starts working only after a few second. I found a solution that blocks the RecyclerView instantaneously:

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

y
ypresto

Override onTouchEvent() and onInterceptTouchEvent() and return false if you don't need OnItemTouchListener at all. This does not disable OnClickListeners of 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;
    }
}

a
anas

Just add this to your recycleview in xml

 android:nestedScrollingEnabled="false"

like this

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

awesome solution
P
Pankaj Talaviya

Add

android:descendantFocusability="blocksDescendants"

in your child of SrollView or NestedScrollView (and parent of ListView, recyclerview and gridview any one)


you saved my life
H
Hasim D

At activity's onCreate method, you can simply do:

recyclerView.stopScroll()

and it stops scrolling.


A
Aladdin

I have been struggling in this issue for some hour, So I would like to share my experience, For the layoutManager solution it is fine but if u want to reEnable scrolling the recycler will back to top.

The best solution so far (for me at least) is using @Zsolt Safrany methode but adding getter and setter so you don't have to remove or add the OnItemTouchListener.

As follow

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

Usage

RecyclerViewDisabler disabler = new RecyclerViewDisabler(true);
feedsRecycler.addOnItemTouchListener(disabler);

// TO ENABLE/DISABLE JUST USE THIS
disabler.setEnable(enable);

A
Atefeh Srch

You can add this line after setting your adapter

ViewCompat.setNestedScrollingEnabled(recyclerView, false);

Now your recyclerview will work with smooth scrolling


T
Tim Jorjev

There is a more straightforward way to disable scrolling (technically it is more rather interception of a scrolling event and ending it when a condition is met), using just standard functionality. RecyclerView has the method called addOnScrollListener(OnScrollListener listener), and using just this you can stop it from scrolling, just so:

recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
    @Override
    public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
        super.onScrollStateChanged(recyclerView, newState);
        if (viewModel.isItemSelected) {
            recyclerView.stopScroll();
        }
    }
});

Use case: Let's say that you want to disable scrolling when you click on one of the items within RecyclerView so you could perform some actions with it, without being distracted by accidentally scrolling to another item, and when you are done with it, just click on the item again to enable scrolling. For that, you would want to attach OnClickListener to every item within RecyclerView, so when you click on an item, it would toggle isItemSelected from false to true. This way when you try to scroll, RecyclerView will automatically call method onScrollStateChanged and since isItemSelected set to true, it will stop immediately, before RecyclerView got the chance, well... to scroll.

Note: for better usability, try to use GestureListener instead of OnClickListener to prevent accidental clicks.


stopScroll() is not to freeze the scroll, it's just to halt/stop scrolling recyclerview.
@FARID, yes, this method just stops any scroll in progress, and in this particular case, it does that every time when a user tries to scroll RecyclerView content with isItemSelected set to true. Otherwise, you would need a custom RecyclerView to disable scrolling for good and I wanted to avoid it since I needed just a little extra functionality, which this solution provides.
N
Natan Lotério

For whom want's to just prevent the user to scroll the RecyclerView, without loose the smoothScrollToPosition or any other "go to position" method, I'd recommend rather extending the RecyclerView class, overriding the onTouchEvent. Like this:

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

b
bwhite

Here is how I did it with data binding:

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

In place of the "true" I used a boolean variable that changed based on a condition so that the recycler view would switch between being disabled and enabled.


G
Guy

For stop scrolling by touch but keep scrolling via commands :

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

K
Kishan Viramgama

You can creat a Non Scrollable Recycler View which extends a Recycler View class, as follows:

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