Control which directions the ViewPager can be scrolled, dynamically with UI feedback

13,010

Solution 1

When I faced a similar problem, I made my own Listener with a gestureDetector and made it not respond to swiping at all, you can easily adjust it to not respond to swiping to the right only under some condition. This should answer your numbers 1 and 2.

This is the listener:

public class OnSwipeTouchListener implements OnTouchListener {

@SuppressWarnings("deprecation")
private final GestureDetector gestureDetector = new GestureDetector(new GestureListener());

public boolean onTouch(final View v, final MotionEvent event) {
    return gestureDetector.onTouchEvent(event);
}

private final class GestureListener extends SimpleOnGestureListener {

    private static final int SWIPE_THRESHOLD = 100;
    private static final int SWIPE_VELOCITY_THRESHOLD = 100;

    @Override
    public boolean onDown(MotionEvent e) {
        return true;
    }
    @Override
    public boolean onSingleTapConfirmed(MotionEvent e) {
        onTouch(e);
        return true;
    }


    @Override
    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
        boolean result = false;
        try {
            float diffY = e2.getY() - e1.getY();
            float diffX = e2.getX() - e1.getX();
            if (Math.abs(diffX) > Math.abs(diffY)) {
                if (Math.abs(diffX) > SWIPE_THRESHOLD && Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) {
                    if (diffX > 0) {
                        onSwipeRight();
                    } else {
                        onSwipeLeft();
                    }
                }
            } else {
               // onTouch(e);
            }
        } catch (Exception exception) {
            exception.printStackTrace();
        }
        return result;
    }
}
public void onTouch(MotionEvent e) {
}
public void onSwipeRight() {
//call this only if your condition was set
}

public void onSwipeLeft() {
//nothing, this means,swipes to left will be ignored
}

public void onSwipeTop() {
}

public void onSwipeBottom() {
}
}

and this is an example of use :

viewPager.setOnTouchListener(new OnSwipeTouchListener() {
public void onSwipeRight() {
  if(your conditionIsMet){
// move your pager view to the right
     }
   }

});

There might be a simpler and more elegant solution, but this worked well for me.

Solution 2

I have been googling high and low for a good way to solving this problem... I tried overriding ontouchevent etc...

I decided this was the best solution for me (and less hacky).. was just to implement a maximum or minimum tracker and set it to current page

        maxPageToScroll = viewPager.getCurrentItem();
        minPageToScroll = viewPager.getCurrentItem();

and then add to "onPageSelected" method:

public void onPageSelected(int position) {

        if (position < minPageToScroll) {
            viewPager.setCurrentItem(maxPageToScroll);
        }
        else if (position > maxPageToScroll) {
            viewPager.setCurrentItem(maxPageToScroll);
            // you can alert here that user needs to do certain actions before proceeding
        }
        else { 
            // enter normal commands here
        }

consequently, when conditions are met you simply increase the maxpage by one..

        maxPageToScroll = viewPager.getCurrentItem() + 1;

note that this doesn't prevent user form scrolling... it just prevents view pager from changing pages.

Solution 3

I don't think extending ViewPager is needed. What you need to do is to modify the list of pages provided to the PagerAdapter on the fly. Meaning that, you check if a certain page is marked "completed" and only then add the next page to the list. That's how ViewPager works.

Take a look at Roman Nurik's wizard-pager implementation. In MainActivity there is a method called recalculateCutOffPage which does what I said.

Solution 4

you can use your custom view pager

public class CustomViewPager extends ViewPager
{   
float mStartDragX;
OnSwipeOutListener mListener;
Context context;
public static boolean enabled;

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


@Override
public boolean onTouchEvent(MotionEvent event)
{

    if (enabled) //view pager scrolling enable if true
    {
        return super.onTouchEvent(event);
    }
    return false;
}


@Override
public boolean onInterceptTouchEvent(MotionEvent ev)
{

    if (enabled)enter code here
    {
        return super.onInterceptTouchEvent(ev);
    }
    else  // view pager disable scrolling 
    {
        float x = ev.getX();
        switch (ev.getAction())
        {
            case MotionEvent.ACTION_DOWN:
                mStartDragX = x;

                break;
            case MotionEvent.ACTION_MOVE:
                if (mStartDragX < x - 100)//100 value velocity
                {
                    //Left scroll 
                    //return super.onInterceptTouchEvent(ev); 
                }
                else
                    if (mStartDragX > x + 100)
                    {
                        //Right scroll 
                        //return super.onInterceptTouchEvent(ev);
                    }
                break;
        }
    }

    return false;

}


public void setOnSwipeOutListener(OnSwipeOutListener listener)
{
    mListener = listener;
}

public interface OnSwipeOutListener
{

    public void onSwipeOutAtStart();


    public void onSwipeOutAtEnd();
}

}

Share:
13,010

Related videos on Youtube

Tim Kist
Author by

Tim Kist

Android developer. I like making pretty code to make pretty apps.

Updated on September 15, 2022

Comments

  • Tim Kist
    Tim Kist over 1 year

    I'm working on an app which uses many pages to swipe through, however at some pages I would like to be able to prevent scrolling to the next page until they've selected something.

    At the moment I've extended ViewPager:

    /* (non-Javadoc)
     * @see android.support.v4.view.ViewPager#onInterceptTouchEvent(android.view.MotionEvent)
     */
    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        if (this.canGoLeft || this.canGoRight) {
            return super.onInterceptTouchEvent(event);
        }
    
        return false;
    }
    
    /* (non-Javadoc)
     * @see android.support.v4.view.ViewPager#onTouchEvent(android.view.MotionEvent)
     */
    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        boolean lockScroll = false;
        switch (ev.getAction()) {
        case MotionEvent.ACTION_DOWN:
            lastX = ev.getX();
            lockScroll = false;
            return super.onTouchEvent(ev);
        case MotionEvent.ACTION_MOVE:
            if (canGoLeft && lastX < ev.getX()) {
                lockScroll = false;
            } else if (canGoRight && lastX > ev.getX()) {
                lockScroll = false;
            } else {
                lockScroll = true;
            }
    
    
            lastX = ev.getX();
    
            break;
        }
    
        lastX = ev.getX();
    
        if (lockScroll) {
            //make callback to prevent movement
            if (mOnPreventCallback != null) {
                mOnPreventCallback.onPreventMovement();
            }
            return false;
        } else {
            return super.onTouchEvent(ev);
        }
    }
    

    Note: I've tried to implement prevent swiping to previous however you can still swipe to the previous page but you need to swipe from the uttermost left side and swipe right a bit. It's temperamental but it does not prevent swiping completely.

    Three things I want to do here:

    1. Prevent scrolling to next screen until they have selected an item or something
    2. Prevent scrolling to previous screen throughout (partially implemented)
    3. Give feedback to the user to show that scrolling is disabled, like the way Android shows the top and bottom screens with a gradient.
  • Tim Kist
    Tim Kist over 10 years
    This is what I'm looking in to at the moment. Thanks.
  • Tim Kist
    Tim Kist over 9 years
    That's OK to prevent swipes to right, but what about left?
  • Tim Kist
    Tim Kist over 9 years
    Solves being able to prevent swipes to the right, but not the left. My solution was a combination of WizardPager and a custom ViewPager
  • Aviv Mor
    Aviv Mor over 9 years
  • Zvi
    Zvi almost 8 years
    The problem with this solution is that the tab indicator (the underline under the tab title) moves to the new page although the page is not displayed