Swipe Direction in ViewPager
Asked Answered
U

10

21

I have an Android app that uses ViewPager and FragmentStatePagerAdapter. I however need to add a new feature in which I have to know the swipe direction to move from one fragment view to another, left or right. (i.e, when I swipe left, I would do one thing, and when I swipe right I would do something else).

Please note, I do not need the events to happen AFTER the swipe is over. I have to do those events as the swipe occurs. I thought of using setOnPageChangeListener() but it does not tell me swipe direction. Can you advise me please on how to figure out swipe direction?

Thanks.

Unionism answered 13/7, 2014 at 10:6 Comment(0)
S
11

I ran into this issue and need to know the direction of the swipe as it was happening in order to prevent swiping in a certain direction. Here is how I solved my problem, hopefully it is helpful to someone. I found all of the previous examples inefficient for what I needed.

If you subclass your ViewPager, you can respond to the onInterceptTouchEvent(MotionEvent event) and onTouchEvent(MotionEvent event) methods of the ViewPager

@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
    boolean wasSwipeToRight = this.wasSwipeToRightEvent(event));
    // Do what you want here with left/right swipe

    return super.onInterceptTouchEvent(event);
}

@Override
public boolean onTouchEvent(MotionEvent event) {
    boolean wasSwipeToRight = this.wasSwipeToRightEvent(event));
    // Do what you want here with left/right swipe

    return super.onTouchEvent(event);
}

Here is the method which is called to determine the direction of the event.

// Stores the starting X position of the ACTION_DOWN event
float downX;

/**
 * Checks the X position value of the event and compares it to
 * previous MotionEvents. Returns a true/false value based on if the 
 * event was an swipe to the right or a swipe to the left.
 *
 * @param event -   Motion Event triggered by the ViewPager
 * @return      -   True if the swipe was from left to right. False otherwise
 */
private boolean wasSwipeToRightEvent(MotionEvent event){
    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            downX = event.getX();
            return false;

        case MotionEvent.ACTION_MOVE:
        case MotionEvent.ACTION_UP:
            return downX - event.getX() > 0;

        default:
            return false;
    }
}
Southard answered 5/12, 2015 at 21:18 Comment(2)
downX is always returns 0.0 for meSinghal
@Singhal is event.getX() returning a value when you touch the screen?Southard
L
9

This is simple enough with the introduction of an instance variable to keep track of the current page.

public class MyActivity extends Activity implements ViewPager.OnPageChangeListener {

    private ViewPager viewPager;
    private int currentPosition;


    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // Do trivial stuff
        .
        .
        .
        viewPager.setOnClickListener(this); // doesnt have to be in onCreate
    }

    @Override
    public void onPageSelected(int position) {
        if(currentPosition < position) {
            // handle swipe LEFT
        } else if(currentPosition > position){
            // handle swipe RIGHT
        }
        currentPosition = position; // Update current position
    }
}
Lecturer answered 3/8, 2015 at 17:4 Comment(0)
P
7

Take a look at the onPageSelected(int position) method of ViewPager.OnPageChangeListener. The position gives the index of newly selected page. If you keep a track of the current page index, then you can compare it against the position index to get the left/right direction of the swipe.

Now that you mention OnPageChangeListener doesn't help, consider the onInterceptTouchEvent (MotionEvent ev) method of ViewPager. Take into consideration the MotionEvent's ACTION_DOWN, ACTION_MOVE and ACTION_UP. Hope this helps.

Phenylamine answered 13/7, 2014 at 10:26 Comment(0)
R
4

I use this small implementation to detect if the user is scrolling to the right or to the left during the scrolling itself. I might have some bugs during certain conditions that I am not aware of but it is what I could come up with for what I needed without having to add a touch listener :

viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {

        int SCROLLING_RIGHT = 0;
        int SCROLLING_LEFT = 1;
        int SCROLLING_UNDETERMINED = 2;

        int currentScrollDirection = 2;

        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

            if (isScrollDirectionUndetermined()){
                setScrollingDirection(positionOffset);
            }



            if (isScrollingLeft()){
                Log.i("TabLayout","Scrolling LEFT");
            }
            if (isScrollingRight()){
                Log.i("TabLayout","Scrolling RIGHT");

            }

        }

        private void setScrollingDirection(float positionOffset){
            if ((1-positionOffset)>= 0.5){
                this.currentScrollDirection = SCROLLING_RIGHT;
            }
            else if ((1-positionOffset)<= 0.5){
                this.currentScrollDirection =  SCROLLING_LEFT;
            }
        }

        private boolean isScrollDirectionUndetermined(){
            return currentScrollDirection == SCROLLING_UNDETERMINED;
        }

        private boolean isScrollingRight(){
            return currentScrollDirection == SCROLLING_RIGHT;
        }

        private boolean isScrollingLeft(){
            return currentScrollDirection == SCROLLING_LEFT;
        }

        @Override
        public void onPageSelected(int position) {
        }

        @Override
        public void onPageScrollStateChanged(int state) {
            if (state == ViewPager.SCROLL_STATE_IDLE){
                this.currentScrollDirection = SCROLLING_UNDETERMINED;
            }
        }

    });

We can detect the scroll direction because when the user is scrolling from left to right the positionOffset goes from 0 -> 1 and when scrolling from right to left it goes from 1 -> 0. We can then check that value once at the very beginning of a scroll and reset the flag only at the end of the scroll.

We set the currentScrollDirection to undetermined at position idle instead of onPageSelected because onPageSelected can get called if the user releases in the middle of a scroll and then you would get a false reading.

Roundel answered 26/11, 2017 at 19:52 Comment(0)
D
3

Maybe you can build a listener like this:

buttonIcons.setOnTouchListener(new OnTouchListener() {

        @Override
        public boolean onTouch(final View v, final MotionEvent event) {
            switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                downX = event.getX();

                return true;

            case MotionEvent.ACTION_UP:

                upX = event.getX();
                final float deltaX = downX - upX;

                if (deltaX > 0 && deltaX > SWIPE_MIN_DISTANCE) {

                    final Animation loadInAnimation = AnimationUtils
                            .loadAnimation(
                                    activity.this,
                                    R.anim.slide_in_right);
                    final Animation loadOutAnimation = AnimationUtils
                            .loadAnimation(
                                    activity.this,
                                    R.anim.slide_out_left);

                    viewFlipper.setInAnimation(loadInAnimation);
                    viewFlipper.setOutAnimation(loadOutAnimation);
                    viewFlipper.showPrevious();

                    startSaveButtonAnimation();

                }
                if (deltaX < 0 && -deltaX > SWIPE_MIN_DISTANCE) {

                    final Animation loadInAnimation = AnimationUtils
                            .loadAnimation(
                                    activity.this,
                                    R.anim.slide_in_left);
                    final Animation loadOutAnimation = AnimationUtils
                            .loadAnimation(
                                    activity.this,
                                    R.anim.slide_out_right);

                    viewFlipper.setInAnimation(loadInAnimation);
                    viewFlipper.setOutAnimation(loadOutAnimation);
                    viewFlipper.showNext();

                    startSaveButtonAnimation();

                }
                return true;

            }

            return false;
        }
    });
Diazonium answered 13/7, 2014 at 10:35 Comment(1)
Thanks a lot!. It worked!. I've been struggling with it for a long time!.Anomalous
F
3

You can add an OnPageChangeListner to the ViewPager and do something like this:

mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
        float tempPositionOffset = 0;

        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
            if (position == 0) {
                if (tempPositionOffset < positionOffset) {
                    Log.d("eric", "scrolling left ...");
                } else {
                    Log.d("eric", "scrolling right ...");
                }

                tempPositionOffset = positionOffset;

           Log.d("eric", "position " + position + "; " + " positionOffset " + positionOffset + "; " + " positionOffsetPixels " + positionOffsetPixels + ";");
            }
        }

        @Override
        public void onPageSelected(int position) {

        }

        @Override
        public void onPageScrollStateChanged(int state) {

        }
    });
Face answered 19/4, 2016 at 4:34 Comment(0)
P
2

You actually can find direction from using parameters of onPageScrolled.
Following listener is set to show log message once per direction of net fragment change ( left if left fragments reveals, center if no fragment reveals, right if right fragment reveals).
Class variables, need to keep track

    // -1 - left, 0 - center, 1 - right
    private int scroll = 0; 
    // set only on `onPageSelected` use it in `onPageScrolled` 
    // if currentPage < page - we swipe from left to right
    // if currentPage == page - we swipe from right to left  or centered
    private int currentPage = 0; 
    // if currentPage < page offset goes from `screen width` to `0`
    // as you reveal right fragment. 
    // if currentPage == page , offset goes from `0` to `screen width`
    // as you reveal right fragment
    // You can use it to see 
    //if user continue to reveal next fragment or moves it back
    private int currentOffset = 0;
    // behaves similar to offset in range `[0..1)`
    private float currentScale = 0;

The listener

pager.addOnPageChangeListener(
            new OnPageChangeListener(){

                @Override
                public void onPageScrolled(int page, float scale, int offset){


                    if(scale != 0f && offset > 10){


                        if(currentOffset > offset && currentScale > scale && (page + 1) == currentPage){


                            if(scroll != -1){
                                newImageSet = true;
                                scroll = -1;
                                Log.e("scroll", "left");
                            }
                        } else if(currentOffset < offset && currentScale < scale && page == currentPage){


                            if(scroll != 1){
                                newImageSet = true;
                                scroll = 1;
                                Log.e("scroll", "right");

                            }
                        }
                        currentOffset = offset;
                        currentScale = scale;



                }


                @Override
                public void onPageSelected(int i){
                    Log.e("scroll","centre");
                    // we are centerd, reset class variables
                    currentPage = i;
                    currentScale = 0;
                    currentOffset = 0;
                    scroll = 0;


                }


                @Override
                public void onPageScrollStateChanged(int i){

                }
            }
        );
Pressey answered 2/10, 2015 at 12:42 Comment(0)
E
2

By using Kotlin and ViewPager2, this is my solution that you can register the OnPageChangeCallback listener as the snippet below (Swipe right only)

val pager = rootView.findViewById<ViewPager2>(R.id.viewPager).apply {
        registerOnPageChangeCallback(object : OnPageChangeCallback(){
            var oldPosition = 0
            override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int
            ) {
                super.onPageScrolled(position, positionOffset, positionOffsetPixels)
                if (position < oldPosition) {
                    setCurrentItem(oldPosition, false)
                } else {
                    oldPosition = position
                }
            }
        })
    }
Equidistance answered 17/6, 2020 at 10:49 Comment(0)
C
0

You can use setOnPageChangeListener() and implement properly onPageScrolled(int position, float positionOffset, int positionOffsetPixels).

You can detect direction by positionOffset and position variable:

public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels)
{
   if(position == mCurrentPosition && positionOffset > 0)
   {
    //swipe right
   }
   else if (positionOffset > 0)
   {
   //swipe left
   }
}
Cogent answered 13/7, 2014 at 10:40 Comment(3)
where are you setting mCurrentPosition ?Compact
you can replace mCurrentPosition by yourViewPager.getCurrentItem()Exaggerated
No that does not work, viewPager.getCurrentItem changes onRelease.Baumann
G
0

I made this class for ViewPager2. Just create an instance and pass it to viewpager2.registerOnPageChangeCallback and don't forget to call viewpager2.unregisterOnPageChangeCallback!

public abstract class ViewPager2SwipeListener extends ViewPager2.OnPageChangeCallback {
  private int prevPosition = -1;
  private int state;

  @Override
  public void onPageScrolled(int newPosition, float positionOffset, int positionOffsetPixels) {
    final boolean positionChanged = prevPosition != -1 && newPosition != prevPosition;

    if (positionChanged && state != ViewPager2.SCROLL_STATE_IDLE) {
      if (prevPosition < newPosition) {
        onPageSwipeLeft(prevPosition, newPosition);
      }
      else {
        onPageSwipeRight(prevPosition, newPosition);
      }
    }

    prevPosition = newPosition;
  }

  @Override
  public void onPageScrollStateChanged(int state) {
    this.state = state;
  }

  public abstract void onPageSwipeRight(int prevPosition, int newPosition);
  public abstract void onPageSwipeLeft(int prevPosition, int newPosition);
}
Grous answered 8/6, 2022 at 11:48 Comment(1)
What is the purpose of last two abstract methods? Probably a hint or two should be added in comments or remove it if a dead code.Molybdous

© 2022 - 2024 — McMap. All rights reserved.