Пролистайте направление в ViewPager

У меня есть приложение для Android, которое использует ViewPager и FragmentStatePagerAdapter. Однако мне нужно добавить новую функцию, в которой я должен знать направление пролистывания, чтобы перейти от одного фрагмента к другому, левому или правому. (то есть, когда я проведу пальцем влево, я сделаю одну вещь, а когда я проведу пальцем вправо, я сделаю что-нибудь еще)

Пожалуйста, обратите внимание, что мне не нужно, чтобы события происходили ПОСЛЕ того, как проведено сканирование. Я должен сделать те события, когда происходит свайп. Я думал об использовании setOnPageChangeListener() но это не говорит мне направление удара. Можете ли вы посоветовать мне, пожалуйста, как определить направление движения?

Благодарю.

10 ответов

Я столкнулся с этой проблемой, и мне нужно знать направление удара, как это происходило, чтобы предотвратить движение в определенном направлении. Вот как я решил свою проблему, надеюсь, она кому-нибудь пригодится. Я нашел все предыдущие примеры неэффективными для того, что мне нужно.

Если вы подкласс своего ViewPager, вы можете ответить на onInterceptTouchEvent(MotionEvent event) а также onTouchEvent(MotionEvent event) методы 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);
}

Вот метод, который вызывается для определения направления события.

// 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;
    }
}

Это достаточно просто с введением переменной экземпляра, чтобы отслеживать текущую страницу.

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
    }
}

Посмотрите на onPageSelected(int position) метод ViewPager.OnPageChangeListener. position дает индекс вновь выбранной страницы. Если вы отслеживаете текущий индекс страницы, то вы можете сравнить его с position Индекс, чтобы получить левое / правое направление прокрутки.

Теперь, когда вы упомянули, что OnPageChangeListener не помогает, рассмотрим onInterceptTouchEvent (MotionEvent ev) метод ViewPager, Примите во внимание ACTION_DOWN, ACTION_MOVE и ACTION_UP MotionEvent. Надеюсь это поможет.

Я использую эту маленькую реализацию, чтобы определить, выполняет ли пользователь прокрутку вправо или влево во время самой прокрутки. У меня могут быть некоторые ошибки в определенных условиях, о которых я не знаю, но это то, что я могу придумать для того, что мне нужно, без необходимости добавления сенсорного прослушивателя:

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

    });

Мы можем определить направление прокрутки, потому что когда пользователь прокручивает слева направо, positionOffset идет от 0 -> 1, а при прокрутке справа налево - от 1 -> 0. Затем мы можем проверить это значение один раз в самом начале прокрутки и сбросить флаг только в конце прокрутки.

Мы устанавливаем currentScrollDirection неопределенным в положении холостого хода вместо onPageSelected так как onPageSelected может быть вызван, если пользователь отпускает в середине прокрутки, а затем вы получите ложное чтение.

Может быть, вы можете создать слушателя так:

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

Используя Kotlin и ViewPager2, это мое решение, в котором вы можете зарегистрировать прослушиватель OnPageChangeCallback как фрагмент ниже (смахните только вправо)

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

Вы можете добавить OnPageChangeListner к ViewPager и сделать что-то вроде этого:

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) {

        }
    });

Вы можете найти направление, используя параметры onPageScrolled,
Следующий слушатель настроен на показ лог-сообщения один раз в каждом направлении изменения фрагмента сети (слева, если обнаружены фрагменты слева, в центре, если фрагменты не обнаружены, справа, если фрагмент справа обнаружен).
Переменные класса, нужно отслеживать

    // -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;

Слушатель

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){

                }
            }
        );

Ты можешь использовать setOnPageChangeListener() и правильно реализовать onPageScrolled(int position, float positionOffset, int positionOffsetPixels),

Вы можете определить направление по позиционированию и переменной положения:

public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels)
{
   if(position == mCurrentPosition && positionOffset > 0)
   {
    //swipe right
   }
   else if (positionOffset > 0)
   {
   //swipe left
   }
}

Я сделал этот класс для ViewPager2. Просто создайте экземпляр и передайте егоviewpager2.registerOnPageChangeCallbackи не забудь позвонить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);
}
Другие вопросы по тегам