4

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
  • 1,164
  • 1
  • 14
  • 38

5 Answers5

4

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.

slezadav
  • 6,104
  • 7
  • 40
  • 61
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.

BtzX
  • 21
  • 2
  • 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 – Zvi May 26 '16 at 16:51
1

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.

Nimrod Dayan
  • 3,050
  • 4
  • 29
  • 45
1

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

}

Dedaniya HirenKumar
  • 2,980
  • 2
  • 22
  • 25
-1

I implmented viewpager for a wizard with a max page that the user can't pass it.

At the end the solution was in the adapter. I changed the count of the PagerAdapter and this way blocks the user from passing the max page:

@Override
public int getCount() {
    return mProgress; //max page + 1
}

When the user progresses to the next page:

private void setWizardProgress(int progress) {
    if(progress > mProgress) {
        mProgress = progress;
        mWizardPagerAdapter.notifyDataSetChanged();
    }
}

This way when the user is at max page he can't scroll to the right until the progress is updated.

Aviv Mor
  • 469
  • 6
  • 6
  • That's OK to prevent swipes to right, but what about left? – Tim Kist Oct 02 '14 at 11:47
  • If you want to disable swiping completely look here [http://stackoverflow.com/questions/9650265/how-do-disable-paging-by-swiping-with-finger-in-viewpager-but-still-be-able-to-s](http://stackoverflow.com/questions/9650265/how-do-disable-paging-by-swiping-with-finger-in-viewpager-but-still-be-able-to-s) – Aviv Mor Nov 04 '14 at 15:06