I cannot disable scrolling in the RecyclerView
. I tried calling rv.setEnabled(false)
but I can still scroll.
How can I disable scrolling?
RecyclerView
if you do not want scrolling?
View
over top of the RecyclerView
, toggling between VISIBLE
and GONE
as needed, but off the cuff your approach seems reasonable.
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 } }
The real answer is
recyclerView.setNestedScrollingEnabled(false);
More info in documentation
NestedScrollView
: android.support.v4.widget.NestedScrollView
, as described here, by chessdork: stackoverflow.com/questions/37846245/…
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">
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
This works for me:
recyclerView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
return true;
}
});
Custom view 'RecyclerView' has setOnTouchListener called on it but does not override performClick
return e.getAction() == MotionEvent.ACTION_MOVE;
instead of return true;
so only scroll/swipe events get cancelled.
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)
You can disable scrolling by freezing your RecyclerView.
To freeze: recyclerView.setLayoutFrozen(true)
To unfreeze: 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;
}
});
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
...
...
/>
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);
There is a simple answer.
LinearLayoutManager lm = new LinearLayoutManager(getContext()) {
@Override
public boolean canScrollVertically() {
return false;
}
};
The above code disables RecyclerView's verticall scrolling.
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()
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
}
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);
Another alternative is setLayoutFrozen
, but it comes with a bunch of other side effects.
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);
}
}
}
});
}
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) {
}
});
You should just add this line:
recyclerView.suppressLayout(true)
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) {
}
});
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;
}
}
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">
Add
android:descendantFocusability="blocksDescendants"
in your child of SrollView or NestedScrollView (and parent of ListView, recyclerview and gridview any one)
At activity's onCreate method, you can simply do:
recyclerView.stopScroll()
and it stops scrolling.
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);
You can add this line after setting your adapter
ViewCompat.setNestedScrollingEnabled(recyclerView, false);
Now your recyclerview will work with smooth scrolling
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.
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.
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;
}
}
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.
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;
}
});
}
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();
}
}
Success story sharing