154

I have a ViewPager which instantiates a View. I'd like to disable both the scrolling of the viewpager and the child buttons momentarily while a search result is returned to the view. I've calling viewPager.setEnabled(false) but this doesn't disable it.

Anyone got any ideas?

slayton
  • 20,123
  • 10
  • 60
  • 89
mAndroid
  • 5,087
  • 6
  • 25
  • 33
  • 2
    Possible duplicate of [How do disable paging by swiping with finger in ViewPager but still be able to swipe programmatically?](http://stackoverflow.com/questions/9650265/how-do-disable-paging-by-swiping-with-finger-in-viewpager-but-still-be-able-to-s) – Violet Giraffe Jul 01 '16 at 13:01

16 Answers16

380

A simple solution is to create your own subclass of ViewPager that has a private boolean flag, isPagingEnabled. Then override the onTouchEvent and onInterceptTouchEvent methods. If isPagingEnabled equals true invoke the super method, otherwise return.

public class CustomViewPager extends ViewPager {

    private boolean isPagingEnabled = true;

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

    public CustomViewPager(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        return this.isPagingEnabled && super.onTouchEvent(event);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        return this.isPagingEnabled && super.onInterceptTouchEvent(event);
    }

    public void setPagingEnabled(boolean b) {
        this.isPagingEnabled = b;
    }
}

Then in your Layout.XML file replace any <com.android.support.V4.ViewPager> tags with <com.yourpackage.CustomViewPager> tags.

This code was adapted from this blog post.

Dan Cornilescu
  • 39,470
  • 12
  • 57
  • 97
slayton
  • 20,123
  • 10
  • 60
  • 89
  • 2
    greate solution, works fine. If you want to stop swiping like a monkey you can override a OnPageChangeListener with @Override public void onPageScrollStateChanged(final int state) { switch (state) { case ViewPager.SCROLL_STATE_SETTLING: mPager.setPagingEnabled(false); break; case ViewPager.SCROLL_STATE_IDLE: mPager.setPagingEnabled(true); break; } // noop } – Informatic0re Jul 20 '12 at 09:41
  • 13
    P.S: In XML, use – thecr0w Oct 28 '12 at 14:11
  • to add another similar solution, you can use the library called "JazzyViewPager" : https://github.com/jfeinstein10/JazzyViewPager , and just call setPagingEnabled(boolean) – android developer Feb 13 '14 at 09:52
  • is it possible to stop scrolling right only? i can scrolling left only – Saif Hamed Aug 30 '14 at 23:44
  • It is still possible to "scroll" by focusing elements (buttons, etc.) in the pages when using a game controller or keyboard for instance. How to disable that completely? I guess it's another type of event that should be intercepted... – David Ferrand Jan 23 '15 at 17:45
  • Yes, you must set the isPagingEnabled variable to false in the CustomViewPager class, change the tag in you layout .xml file to your CustomViewPager class ****AND**** in the .java file that you declare the view pager view, you must import the custom class, replace import android.support.v4.view.ViewPager; for import ******.CustomViewPager; – flagg327 Jun 16 '16 at 04:40
  • this will work, we can use this code into our custom view – Rakshith Kumar Feb 24 '17 at 12:34
  • Warning "Method 'setPagingEnabled(boolean)' is never used". Do I even need this code or can I delete it? Will anything be affected negatively if I do? – iBEK Oct 06 '17 at 01:34
  • Opposed to the `beginFakeDrag()` trick, this custom viewpager completely prevents its touch events. Great solution. – Erlend K.H. Jan 06 '19 at 18:52
  • To prevent "scroll" by focusing elements with a controller or a keyboard just set: android:focusable="false" to all your views on the .xml – Luis lp Sep 04 '19 at 00:10
36

There is an easy fix for this one:

When you want to disable the viewpager scrolling then:

mViewPager.setOnTouchListener(new OnTouchListener() {

   public boolean onTouch(View arg0, MotionEvent arg1) {
      return true;
   }
}); 

And when you want to re-enable it then:

mViewPager.setOnTouchListener(null);

That will do the trick.

סטנלי גרונן
  • 2,917
  • 23
  • 46
  • 68
Akhil Sekharan
  • 12,467
  • 7
  • 40
  • 57
  • 52
    Almost, but not quite. The touch down still happens, so you can move pages by doing lots of small swipes. – Flynny75 Dec 06 '12 at 00:26
  • @Akhil Sekharan Was facing the problem exactly what Flynny is facing when using this answer.. but fortunately don't know how suddenly that error has gone.. :) and worked like a charm !! – Zombie Dec 06 '12 at 06:49
  • You can do the lots of little swipes so you still have to over public boolean onInterceptTouchEvent(MotionEvent event) { ride as per slatons answer – Ryan Heitner Oct 27 '13 at 12:29
29

Here is my light weight variant of slayton's answer:

public class DeactivatableViewPager extends ViewPager {
    public DeactivatableViewPager(Context context) {
        super(context);
    }

    public DeactivatableViewPager(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        return !isEnabled() || super.onTouchEvent(event);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        return isEnabled() && super.onInterceptTouchEvent(event);
    }
}

With my code you can disable the paging with setEnable().

rekire
  • 47,260
  • 30
  • 167
  • 264
  • Overriding `onInterceptTouchEvent()` will prevent child views to receive input. In most cases this is __not__ the intended behavior. – Alex Semeniuk Jul 16 '19 at 14:25
21

I found a simple solution.

//disable swiping
mViewPager.beginFakeDrag();

//enable swiping
mViewPager.endFakeDrag();
Yessy
  • 267
  • 2
  • 2
18

In my case of using ViewPager 2 alpha 2 the below snippet works

viewPager.isUserInputEnabled = false

Ability to disable user input (setUserInputEnabled, isUserInputEnabled)

refer to this for more changes in viewpager2 1.0.0-alpha02

Also some changes were made latest version ViewPager 2 alpha 4

orientation and isUserScrollable attributes are no longer part of SavedState

refer to this for more changes in viewpager2#1.0.0-alpha04

Piyush Johnson
  • 181
  • 1
  • 3
17

I suggest another way to solve to this problem. The idea is wrapping your viewPager by a scrollView, so that when this scrollView is non-scrollable, your viewPager is non-scrollable too.

Here is my XML layout:

        <HorizontalScrollView
            android:id="@+id/horizontalScrollView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:fillViewport="true" >

            <android.support.v4.view.ViewPager
                android:id="@+id/viewPager"
                android:layout_width="wrap_content"
                android:layout_height="match_parent" />

        </HorizontalScrollView>

No code needed.

Works fine for me.

M. Marmor
  • 390
  • 4
  • 9
5

To disable swipe

 mViewPager.beginFakeDrag();

To enable swipe

 mViewPager.endFakeDrag();
Isaias Carrera
  • 675
  • 9
  • 9
3

The best solution that worked for me is:

public class DeactivatedViewPager extends ViewPager {

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

    public DeactivatedViewPager (Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean canScrollHorizontally(int direction) {
        return false;
    }
}

After this the scroll will be disabled by touch and then you can still use setCurrentItem method to change page.

Pradeep Kumar Kushwaha
  • 2,231
  • 3
  • 23
  • 34
3

Here's an answer in Kotlin and androidX

import android.content.Context
import android.util.AttributeSet
import android.view.MotionEvent
import androidx.viewpager.widget.ViewPager

class DeactivatedViewPager @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null
) : ViewPager(context, attrs) {

    var isPagingEnabled = true

    override fun onTouchEvent(ev: MotionEvent?): Boolean {
        return isPagingEnabled && super.onTouchEvent(ev)
    }

    override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
        return isPagingEnabled && super.onInterceptTouchEvent(ev)
    }
}
Pasi Matalamäki
  • 1,843
  • 17
  • 14
2

How about this :
I used CustomViewPager
and next, override scrollTo method
I checked the movement when doing a lot of small swipes, it doesn't scroll to other pages.

public class CustomViewPager extends ViewPager {

    private boolean enabled;

    public CustomViewPager(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.enabled = true;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (this.enabled) {
            return super.onTouchEvent(event);
        }

        return false;
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        if (this.enabled) {
            return super.onInterceptTouchEvent(event);
        }

        return false;
    }

    public void setPagingEnabled(boolean enabled) {
        this.enabled = enabled;
    } 

    @Override
    public void scrollTo(int x, int y) {
       if(enabled) {
          super.scrollTo(x, y);
       }
    }
}
Arda Kara
  • 501
  • 6
  • 24
godbye
  • 154
  • 1
  • 4
  • I had the problem that the viewpager was still doing little swipes, but overriding `View.scrollTo(int x, int y)` solved this problem for me. **Thanks** – Palm Mar 08 '18 at 19:30
  • It causes some strange behavior in some cases. Like going to the void and leaving just a blank page '-' – Ricardo A. May 21 '19 at 14:47
2
import android.content.Context;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.animation.DecelerateInterpolator;
import android.widget.Scroller;
import java.lang.reflect.Field;

public class NonSwipeableViewPager extends ViewPager {

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

public NonSwipeableViewPager(Context context, AttributeSet attrs) {
    super(context, attrs);
}

@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
    // stop swipe 
    return false;
}

@Override
public boolean onTouchEvent(MotionEvent event) {
    // stop switching pages
    return false;
}

private void setMyScroller() {
    try {
        Class<?> viewpager = ViewPager.class;
        Field scroller = viewpager.getDeclaredField("mScroller");
        scroller.setAccessible(true);
        scroller.set(this, new MyScroller(getContext()));
    } catch (Exception e) {
        e.printStackTrace();
    }
}

public class MyScroller extends Scroller {
    public MyScroller(Context context) {
        super(context, new DecelerateInterpolator());
    }

    @Override
    public void startScroll(int startX, int startY, int dx, int dy, int 
duration) {
        super.startScroll(startX, startY, dx, dy, 350 /*1 secs*/);
    }
}
}

Then in your Layout.XML file replace any --- com.android.support.V4.ViewPager --- tags with --- com.yourpackage.NonSwipeableViewPager --- tags.

Same solution but in Kotlin:

class NonSwipeableViewPager constructor(
    context: Context,
    attrs: AttributeSet?
) : ViewPager(context, attrs) {

    private var isPagingEnabled = true

    override fun onTouchEvent(event: MotionEvent?): Boolean {
        return isPagingEnabled && super.onTouchEvent(event)
    }

    override fun onInterceptTouchEvent(event: MotionEvent?): Boolean {
        return isPagingEnabled && super.onInterceptTouchEvent(event)
    }

    fun setPagingEnabled(isEnabled: Boolean) {
        isPagingEnabled = isEnabled
    }
}
Boken
  • 4,825
  • 10
  • 32
  • 42
2

I created a Kotlin version based on converting this answer from Java: https://stackoverflow.com/a/13437997/8023278

There is no built in way to disable swiping between pages of a ViewPager, what's required is an extension of ViewPager that overrides onTouchEvent and onInterceptTouchEvent to prevent the swiping action. To make it more generalised we can add a method setSwipePagingEnabled to enable/disable swiping between pages.

class SwipeLockableViewPager(context: Context, attrs: AttributeSet): ViewPager(context, attrs) {
    private var swipeEnabled = false

    override fun onTouchEvent(event: MotionEvent): Boolean {
        return when (swipeEnabled) {
            true -> super.onTouchEvent(event)
            false -> false
        }
    }

    override fun onInterceptTouchEvent(event: MotionEvent): Boolean {
        return when (swipeEnabled) {
            true -> super.onInterceptTouchEvent(event)
            false -> false
        }
    }

    fun setSwipePagingEnabled(swipeEnabled: Boolean) {
        this.swipeEnabled = swipeEnabled
    }
}

Then in our layout xml we use our new SwipeLockableViewPager instead of the standard ViewPager

<mypackage.SwipeLockableViewPager 
        android:id="@+id/myViewPager" 
        android:layout_height="match_parent" 
        android:layout_width="match_parent" />

Now in our activity/fragment we can call myViewPager.setSwipePagingEnabled(false) and users won't be able to swipe between pages


UPDATE

As of 2020 we now have ViewPager2. If you migrate to ViewPager2 there is a built in method to disable swiping: myViewPager2.isUserInputEnabled = false

Matt Smith
  • 836
  • 10
  • 21
1

New class ViewPager2 from androidx allows to disable scrolling with method setUserInputEnabled(false)

private val pager: ViewPager2 by bindView(R.id.pager)

override fun onCreate(savedInstanceState: Bundle?) {
    pager.isUserInputEnabled = false
}
shushper
  • 1,049
  • 7
  • 10
0

The answer of slayton works fine. If you want to stop swiping like a monkey you can override a OnPageChangeListener with

@Override public void onPageScrollStateChanged(final int state) { 
        switch (state) { 
            case ViewPager.SCROLL_STATE_SETTLING: 
                mPager.setPagingEnabled(false); 
                break; 
            case ViewPager.SCROLL_STATE_IDLE: 
                mPager.setPagingEnabled(true); 
                break; 
        } 
    }

So you can only swipe side by side

Informatic0re
  • 6,300
  • 5
  • 41
  • 56
0

In viewPager2 by disabling user input, you can prevent the user from swiping horizontally.

viewPager.isUserInputEnabled = false
Maede Jalali
  • 306
  • 2
  • 6
0

This is working for me :- (for ViewPager2)

viewPager.isUserInputEnabled = false
avariant
  • 2,234
  • 5
  • 25
  • 33